HTML5移動遊戲開發高階程式設計 3:試飛結束,向移動進發
3.1 引言
新增觸控控制元件以及根據裝置調整遊戲介面尺寸。
3.2 新增觸控控制元件
繪製控制元件:為了使在移動裝置上玩遊戲稱為可能,一種常見的解決方案是在螢幕上新增一些可視觸控控制元件,這些控制元件可由位於頁面底部的三個方形按鈕組成。
遊戲需要處理不同的螢幕解析度,因此,遊戲不會繪製固定大小的輸入方塊,而會基於遊戲介面的寬度調整方塊的尺寸。根據一些非正式測試可知,把遊戲的介面寬度分成5個區就足以獲得很好的效果了,按鈕要大到能夠方便地單擊,但又不能過多佔用螢幕的實際實際使用面積。
TouchControls物件:
響應觸控事件:touchstart、touchmove和touchend。
觸控事件的屬性:
- event.touches:當前發生在裝置上的所有觸控
- event.targetTouches:發生在作為觸發事件物件的同一個DOM上的所有觸控
- event.changedTouches:在這一事件中發生了變化的所有觸控
targetTouches用於左邊控制移動的兩個按鈕,你希望使用者能夠按下這兩個按鈕之一來進行向左或向右移動。所以,每發生一個觸控事件,遊戲就會檢視當前是否有觸控擊中了這兩個按鈕中的任一個。若是,則標記該按鈕被按下,即使觸發事件的觸控並未擊中任何按鈕,遊戲也會進行此番檢查。
3.3 最大化遊戲介面
設定視口:不想讓使用者縮放頁面,可加入
<meta name="viewport" content="width=device-width, user-scalable=0,minimum-scale=1.0,maximum-scale=1.0"/>
調整畫布尺寸:步驟如下
檢查瀏覽器是否支援觸控事件
若螢幕大於最大尺寸或沒有觸控支援,提前退出
檢查使用者是否處於橫屏模式下
若是,要求他們旋轉瀏覽器
將容器的尺寸調整為大於頁面,以便刪除位址列
稍微滾動視窗,藉此強制刪除位址列
將容器的尺寸設定成與視窗大小相吻合
檢查遊戲是否在(平板電腦一類的)較大裝置上執行
若是,把檢視的大小設定成效能畫素大小的兩倍
否則,將畫布設定成與視窗大小相吻合
最後,將畫布設定成使用絕對定位,把畫布的絕對位置設定成視窗的左上角
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
3.4 新增得分
只需在遊戲中新增一個新的遊戲皮膚即可GamePoints物件
3.5 使之成為公平的戰鬥
賦予敵方飛船一點回擊的火力
EnemyMissile物件
index.html
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>Alien Invasion</title>
<link rel="stylesheet" href="base.css" type="text/css" />
<link href='http://fonts.googleapis.com/css?family=Bangers' rel='stylesheet' type='text/css'>
<meta name="viewport" content="width=device-width, user-scalable=0, minimum-scale=1.0, maximum-scale=1.0"/>
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
</head>
<body>
<div id='container'>
<canvas id='game' width='320' height='480'></canvas>
</div>
<script src='engine.js'></script>
<script src='game.js'></script>
</body>
</html>
base.css
/* http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126
License: none (public domain)
*/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
/* Center the container */
#container {
padding-top:50px;
margin:0 auto;
width:480px;
}
/* Give canvas a background */
canvas {
background-color:black;
}
engine.js
var Game = new function() {
var boards = [];
// Game Initialization
this.initialize = function(canvasElementId,sprite_data,callback) {
this.canvas = document.getElementById(canvasElementId);
this.playerOffset = 10;
this.canvasMultiplier= 1;
this.setupMobile();
this.width = this.canvas.width;
this.height= this.canvas.height;
this.ctx = this.canvas.getContext && this.canvas.getContext('2d');
if(!this.ctx) { return alert("Please upgrade your browser to play"); }
this.setupInput();
this.loop();
if(this.mobile) {
this.setBoard(4,new TouchControls());
}
SpriteSheet.load(sprite_data,callback);
};
// Handle Input
var KEY_CODES = { 37:'left', 39:'right', 32 :'fire' };
this.keys = {};
this.setupInput = function() {
window.addEventListener('keydown',function(e) {
if(KEY_CODES[event.keyCode]) {
Game.keys[KEY_CODES[event.keyCode]] = true;
e.preventDefault();
}
},false);
window.addEventListener('keyup',function(e) {
if(KEY_CODES[event.keyCode]) {
Game.keys[KEY_CODES[event.keyCode]] = false;
e.preventDefault();
}
},false);
};
// Game Loop
this.loop = function() {
var dt = 30 / 1000;
setTimeout(Game.loop,30);
for(var i=0,len = boards.length;i<len;i++) {
if(boards[i]) {
boards[i].step(dt);
boards[i].draw(Game.ctx);
}
}
};
// Change an active game board
this.setBoard = function(num,board) { boards[num] = board; };
this.setupMobile = function() {
var container = document.getElementById("container"),
hasTouch = !!('ontouchstart' in window),
w = window.innerWidth, h = window.innerHeight;
if(hasTouch) { mobile = true; }
if(screen.width >= 1280 || !hasTouch) { return false; }
if(w > h) {
alert("Please rotate the device and then click OK");
w = window.innerWidth; h = window.innerHeight;
}
container.style.height = h*2 + "px";
window.scrollTo(0,1);
h = window.innerHeight + 2;
container.style.height = h + "px";
container.style.width = w + "px";
container.style.padding = 0;
if(h >= this.canvas.height * 1.75 || w >= this.canvas.height * 1.75) {
this.canvasMultiplier = 2;
this.canvas.width = w / 2;
this.canvas.height = h / 2;
this.canvas.style.width = w + "px";
this.canvas.style.height = h + "px";
} else {
this.canvas.width = w;
this.canvas.height = h;
}
this.canvas.style.position='absolute';
this.canvas.style.left="0px";
this.canvas.style.top="0px";
};
return this;
};
var SpriteSheet = new function() {
this.map = { };
this.load = function(spriteData,callback) {
this.map = spriteData;
this.image = new Image();
this.image.onload = callback;
this.image.src = 'images/sprites.png';
};
this.draw = function(ctx,sprite,x,y,frame) {
var s = this.map[sprite];
if(!frame) frame = 0;
ctx.drawImage(this.image,
s.sx + frame * s.w,
s.sy,
s.w, s.h,
Math.floor(x), Math.floor(y),
s.w, s.h);
};
return this;
};
var TitleScreen = function TitleScreen(title,subtitle,callback) {
var up = false;
this.step = function(dt) {
if(!Game.keys['fire']) up = true;
if(up && Game.keys['fire'] && callback) callback();
};
this.draw = function(ctx) {
ctx.fillStyle = "#FFFFFF";
ctx.textAlign = "center";
ctx.font = "bold 40px bangers";
ctx.fillText(title,Game.width/2,Game.height/2);
ctx.font = "bold 20px bangers";
ctx.fillText(subtitle,Game.width/2,Game.height/2 + 40);
};
};
var GameBoard = function() {
var board = this;
// The current list of objects
this.objects = [];
this.cnt = {};
// Add a new object to the object list
this.add = function(obj) {
obj.board=this;
this.objects.push(obj);
this.cnt[obj.type] = (this.cnt[obj.type] || 0) + 1;
return obj;
};
// Mark an object for removal
this.remove = function(obj) {
var idx = this.removed.indexOf(obj);
if(idx == -1) {
this.removed.push(obj);
return true;
} else {
return false;
}
};
// Reset the list of removed objects
this.resetRemoved = function() { this.removed = []; };
// Removed an objects marked for removal from the list
this.finalizeRemoved = function() {
for(var i=0,len=this.removed.length;i<len;i++) {
var idx = this.objects.indexOf(this.removed[i]);
if(idx != -1) {
this.cnt[this.removed[i].type]--;
this.objects.splice(idx,1);
}
}
};
// Call the same method on all current objects
this.iterate = function(funcName) {
var args = Array.prototype.slice.call(arguments,1);
for(var i=0,len=this.objects.length;i<len;i++) {
var obj = this.objects[i];
obj[funcName].apply(obj,args);
}
};
// Find the first object for which func is true
this.detect = function(func) {
for(var i = 0,val=null, len=this.objects.length; i < len; i++) {
if(func.call(this.objects[i])) return this.objects[i];
}
return false;
};
// Call step on all objects and them delete
// any object that have been marked for removal
this.step = function(dt) {
this.resetRemoved();
this.iterate('step',dt);
this.finalizeRemoved();
};
// Draw all the objects
this.draw= function(ctx) {
this.iterate('draw',ctx);
};
// Check for a collision between the
// bounding rects of two objects
this.overlap = function(o1,o2) {
return !((o1.y+o1.h-1<o2.y) || (o1.y>o2.y+o2.h-1) ||
(o1.x+o1.w-1<o2.x) || (o1.x>o2.x+o2.w-1));
};
// Find the first object that collides with obj
// match against an optional type
this.collide = function(obj,type) {
return this.detect(function() {
if(obj != this) {
var col = (!type || this.type & type) && board.overlap(obj,this);
return col ? this : false;
}
});
};
};
var Sprite = function() { };
Sprite.prototype.setup = function(sprite,props) {
this.sprite = sprite;
this.merge(props);
this.frame = this.frame || 0;
this.w = SpriteSheet.map[sprite].w;
this.h = SpriteSheet.map[sprite].h;
};
Sprite.prototype.merge = function(props) {
if(props) {
for (var prop in props) {
this[prop] = props[prop];
}
}
};
Sprite.prototype.draw = function(ctx) {
SpriteSheet.draw(ctx,this.sprite,this.x,this.y,this.frame);
};
Sprite.prototype.hit = function(damage) {
this.board.remove(this);
};
var Level = function(levelData,callback) {
this.levelData = [];
for(var i =0; i<levelData.length; i++) {
this.levelData.push(Object.create(levelData[i]));
}
this.t = 0;
this.callback = callback;
};
Level.prototype.step = function(dt) {
var idx = 0, remove = [], curShip = null;
// Update the current time offset
this.t += dt * 1000;
// Start, End, Gap, Type, Override
// [ 0, 4000, 500, 'step', { x: 100 } ]
while((curShip = this.levelData[idx]) &&
(curShip[0] < this.t + 2000)) {
// Check if we've passed the end time
if(this.t > curShip[1]) {
remove.push(curShip);
} else if(curShip[0] < this.t) {
// Get the enemy definition blueprint
var enemy = enemies[curShip[3]],
override = curShip[4];
// Add a new enemy with the blueprint and override
this.board.add(new Enemy(enemy,override));
// Increment the start time by the gap
curShip[0] += curShip[2];
}
idx++;
}
// Remove any objects from the levelData that have passed
for(var i=0,len=remove.length;i<len;i++) {
var remIdx = this.levelData.indexOf(remove[i]);
if(remIdx != -1) this.levelData.splice(remIdx,1);
}
// If there are no more enemies on the board or in
// levelData, this level is done
if(this.levelData.length === 0 && this.board.cnt[OBJECT_ENEMY] === 0) {
if(this.callback) this.callback();
}
};
Level.prototype.draw = function(ctx) { };
var TouchControls = function() {
var gutterWidth = 10;
var unitWidth = Game.width/5;
var blockWidth = unitWidth-gutterWidth;
this.drawSquare = function(ctx,x,y,txt,on) {
ctx.globalAlpha = on ? 0.9 : 0.6;
ctx.fillStyle = "#CCC";
ctx.fillRect(x,y,blockWidth,blockWidth);
ctx.fillStyle = "#FFF";
ctx.textAlign = "center";
ctx.globalAlpha = 1.0;
ctx.font = "bold " + (3*unitWidth/4) + "px arial";
ctx.fillText(txt,
x+blockWidth/2,
y+3*blockWidth/4+5);
};
this.draw = function(ctx) {
ctx.save();
var yLoc = Game.height - unitWidth;
this.drawSquare(ctx,gutterWidth,yLoc,"\u25C0", Game.keys['left']);
this.drawSquare(ctx,unitWidth + gutterWidth,yLoc,"\u25B6", Game.keys['right']);
this.drawSquare(ctx,4*unitWidth,yLoc,"A",Game.keys['fire']);
ctx.restore();
};
this.step = function(dt) { };
this.trackTouch = function(e) {
var touch, x;
e.preventDefault();
Game.keys['left'] = false;
Game.keys['right'] = false;
for(var i=0;i<e.targetTouches.length;i++) {
touch = e.targetTouches[i];
x = touch.pageX / Game.canvasMultiplier - Game.canvas.offsetLeft;
if(x < unitWidth) {
Game.keys['left'] = true;
}
if(x > unitWidth && x < 2*unitWidth) {
Game.keys['right'] = true;
}
}
if(e.type == 'touchstart' || e.type == 'touchend') {
for(i=0;i<e.changedTouches.length;i++) {
touch = e.changedTouches[i];
x = touch.pageX / Game.canvasMultiplier - Game.canvas.offsetLeft;
if(x > 4 * unitWidth) {
Game.keys['fire'] = (e.type == 'touchstart');
}
}
}
};
Game.canvas.addEventListener('touchstart',this.trackTouch,true);
Game.canvas.addEventListener('touchmove',this.trackTouch,true);
Game.canvas.addEventListener('touchend',this.trackTouch,true);
Game.playerOffset = unitWidth + 20;
};
var GamePoints = function() {
Game.points = 0;
var pointsLength = 8;
this.draw = function(ctx) {
ctx.save();
ctx.font = "bold 18px arial";
ctx.fillStyle= "#FFFFFF";
var txt = "" + Game.points;
var i = pointsLength - txt.length, zeros = "";
while(i-- > 0) { zeros += "0"; }
ctx.fillText(zeros + txt,10,20);
ctx.restore();
};
this.step = function(dt) { };
};
game.js
var sprites = {
ship: { sx: 0, sy: 0, w: 37, h: 42, frames: 1 },
missile: { sx: 0, sy: 30, w: 2, h: 10, frames: 1 },
enemy_purple: { sx: 37, sy: 0, w: 42, h: 43, frames: 1 },
enemy_bee: { sx: 79, sy: 0, w: 37, h: 43, frames: 1 },
enemy_ship: { sx: 116, sy: 0, w: 42, h: 43, frames: 1 },
enemy_circle: { sx: 158, sy: 0, w: 32, h: 33, frames: 1 },
explosion: { sx: 0, sy: 64, w: 64, h: 64, frames: 12 },
enemy_missile: { sx: 9, sy: 42, w: 3, h: 20, frame: 1 }
};
var enemies = {
straight: { x: 0, y: -50, sprite: 'enemy_ship', health: 10,
E: 100 },
ltr: { x: 0, y: -100, sprite: 'enemy_purple', health: 10,
B: 75, C: 1, E: 100, missiles: 2 },
circle: { x: 250, y: -50, sprite: 'enemy_circle', health: 10,
A: 0, B: -100, C: 1, E: 20, F: 100, G: 1, H: Math.PI/2 },
wiggle: { x: 100, y: -50, sprite: 'enemy_bee', health: 20,
B: 50, C: 4, E: 100, firePercentage: 0.001, missiles: 2 },
step: { x: 0, y: -50, sprite: 'enemy_circle', health: 10,
B: 150, C: 1.2, E: 75 }
};
var OBJECT_PLAYER = 1,
OBJECT_PLAYER_PROJECTILE = 2,
OBJECT_ENEMY = 4,
OBJECT_ENEMY_PROJECTILE = 8,
OBJECT_POWERUP = 16;
var startGame = function() {
Game.setBoard(0,new Starfield(20,0.4,100,true));
Game.setBoard(1,new Starfield(50,0.6,100));
Game.setBoard(2,new Starfield(100,1.0,50));
Game.setBoard(3,new TitleScreen("Alien Invasion",
"Press fire to start playing",
playGame));
};
var level1 = [
// Start, End, Gap, Type, Override
[ 0, 4000, 500, 'step' ],
[ 6000, 13000, 800, 'ltr' ],
[ 10000, 16000, 400, 'circle' ],
[ 17800, 20000, 500, 'straight', { x: 50 } ],
[ 18200, 20000, 500, 'straight', { x: 90 } ],
[ 18200, 20000, 500, 'straight', { x: 10 } ],
[ 22000, 25000, 400, 'wiggle', { x: 150 }],
[ 22000, 25000, 400, 'wiggle', { x: 100 }]
];
var playGame = function() {
var board = new GameBoard();
board.add(new PlayerShip());
board.add(new Level(level1,winGame));
Game.setBoard(3,board);
Game.setBoard(5,new GamePoints(0));
};
var winGame = function() {
Game.setBoard(3,new TitleScreen("You win!",
"Press fire to play again",
playGame));
};
var loseGame = function() {
Game.setBoard(3,new TitleScreen("You lose!",
"Press fire to play again",
playGame));
};
window.addEventListener("load", function() {
Game.initialize("game",sprites,startGame);
});
var Starfield = function(speed,opacity,numStars,clear) {
// Set up the offscreen canvas
var stars = document.createElement("canvas");
stars.width = Game.width;
stars.height = Game.height;
var starCtx = stars.getContext("2d");
var offset = 0;
// If the clear option is set,
// make the background black instead of transparent
if(clear) {
starCtx.fillStyle = "#000";
starCtx.fillRect(0,0,stars.width,stars.height);
}
// Now draw a bunch of random 2 pixel
// rectangles onto the offscreen canvas
starCtx.fillStyle = "#FFF";
starCtx.globalAlpha = opacity;
for(var i=0;i<numStars;i++) {
starCtx.fillRect(Math.floor(Math.random()*stars.width),
Math.floor(Math.random()*stars.height),
2,
2);
}
// This method is called every frame
// to draw the starfield onto the canvas
this.draw = function(ctx) {
var intOffset = Math.floor(offset);
var remaining = stars.height - intOffset;
// Draw the top half of the starfield
if(intOffset > 0) {
ctx.drawImage(stars,
0, remaining,
stars.width, intOffset,
0, 0,
stars.width, intOffset);
}
// Draw the bottom half of the starfield
if(remaining > 0) {
ctx.drawImage(stars,
0, 0,
stars.width, remaining,
0, intOffset,
stars.width, remaining);
}
};
// This method is called to update
// the starfield
this.step = function(dt) {
offset += dt * speed;
offset = offset % stars.height;
};
};
var PlayerShip = function() {
this.setup('ship', { vx: 0, reloadTime: 0.25, maxVel: 200 });
this.reload = this.reloadTime;
this.x = Game.width/2 - this.w / 2;
this.y = Game.height - Game.playerOffset - this.h;
this.step = function(dt) {
if(Game.keys['left']) { this.vx = -this.maxVel; }
else if(Game.keys['right']) { this.vx = this.maxVel; }
else { this.vx = 0; }
this.x += this.vx * dt;
if(this.x < 0) { this.x = 0; }
else if(this.x > Game.width - this.w) {
this.x = Game.width - this.w;
}
this.reload-=dt;
if(Game.keys['fire'] && this.reload < 0) {
Game.keys['fire'] = false;
this.reload = this.reloadTime;
this.board.add(new PlayerMissile(this.x,this.y+this.h/2));
this.board.add(new PlayerMissile(this.x+this.w,this.y+this.h/2));
}
};
};
PlayerShip.prototype = new Sprite();
PlayerShip.prototype.type = OBJECT_PLAYER;
PlayerShip.prototype.hit = function(damage) {
if(this.board.remove(this)) {
loseGame();
}
};
var PlayerMissile = function(x,y) {
this.setup('missile',{ vy: -700, damage: 10 });
this.x = x - this.w/2;
this.y = y - this.h;
};
PlayerMissile.prototype = new Sprite();
PlayerMissile.prototype.type = OBJECT_PLAYER_PROJECTILE;
PlayerMissile.prototype.step = function(dt) {
this.y += this.vy * dt;
var collision = this.board.collide(this,OBJECT_ENEMY);
if(collision) {
collision.hit(this.damage);
this.board.remove(this);
} else if(this.y < -this.h) {
this.board.remove(this);
}
};
var Enemy = function(blueprint,override) {
this.merge(this.baseParameters);
this.setup(blueprint.sprite,blueprint);
this.merge(override);
};
Enemy.prototype = new Sprite();
Enemy.prototype.type = OBJECT_ENEMY;
Enemy.prototype.baseParameters = { A: 0, B: 0, C: 0, D: 0,
E: 0, F: 0, G: 0, H: 0,
t: 0, reloadTime: 0.75,
reload: 0 };
Enemy.prototype.step = function(dt) {
this.t += dt;
this.vx = this.A + this.B * Math.sin(this.C * this.t + this.D);
this.vy = this.E + this.F * Math.sin(this.G * this.t + this.H);
this.x += this.vx * dt;
this.y += this.vy * dt;
var collision = this.board.collide(this,OBJECT_PLAYER);
if(collision) {
collision.hit(this.damage);
this.board.remove(this);
}
if(this.reload <= 0 && Math.random() < (this.firePercentage || 0.01) ) {
this.reload = this.reloadTime;
if(this.missiles == 2) {
this.board.add(new EnemyMissile(this.x+this.w-2,this.y+this.h/2));
this.board.add(new EnemyMissile(this.x+2,this.y+this.h/2));
} else {
this.board.add(new EnemyMissile(this.x+this.w/2,this.y+this.h));
}
}
this.reload-=dt;
if(this.y > Game.height ||
this.x < -this.w ||
this.x > Game.width) {
this.board.remove(this);
}
};
Enemy.prototype.hit = function(damage) {
this.health -= damage;
if(this.health <=0) {
if(this.board.remove(this)) {
Game.points += this.points || 100;
this.board.add(new Explosion(this.x + this.w/2,
this.y + this.h/2));
}
}
};
var EnemyMissile = function(x,y) {
this.setup('enemy_missile',{ vy: 200, damage: 10 });
this.x = x - this.w/2;
this.y = y;
};
EnemyMissile.prototype = new Sprite();
EnemyMissile.prototype.type = OBJECT_ENEMY_PROJECTILE;
EnemyMissile.prototype.step = function(dt) {
this.y += this.vy * dt;
var collision = this.board.collide(this,OBJECT_PLAYER)
if(collision) {
collision.hit(this.damage);
this.board.remove(this);
} else if(this.y > Game.height) {
this.board.remove(this);
}
};
var Explosion = function(centerX,centerY) {
this.setup('explosion', { frame: 0 });
this.x = centerX - this.w/2;
this.y = centerY - this.h/2;
};
Explosion.prototype = new Sprite();
Explosion.prototype.step = function(dt) {
this.frame++;
if(this.frame >= 12) {
this.board.remove(this);
}
};
相關文章
- HTML5移動遊戲開發高階程式設計 11:自建Quintus引擎(3)HTML遊戲開發程式設計UI
- HTML5移動遊戲開發高階程式設計 1:先飛後走,先難後易HTML遊戲開發程式設計
- HTML5移動遊戲開發高階程式設計 2:從玩具到遊戲HTML遊戲開發程式設計
- HTML5移動遊戲開發高階程式設計 9:自建Quintus引擎(1)HTML遊戲開發程式設計UI
- HTML5移動遊戲開發高階程式設計 10:自建Quintus引擎(2)HTML遊戲開發程式設計UI
- HTML5遊戲開發進階 7 :單位智慧移動HTML遊戲開發
- 移動遊戲開發精要遊戲開發
- 《HTML5移動Web開發實戰》——第1章 移動Web設計趨勢HTMLWeb
- 《HTML5移動開發》——1.3 測試工具HTML移動開發
- 程式碼的分離與解耦,向移動架構師進階!解耦架構
- 所見即所得:四款免程式設計移動遊戲開發引擎推薦程式設計遊戲開發
- 移動web開發之移動端真機測試Web
- HTML5移動Web開發指南HTMLWeb
- [.net 物件導向程式設計進階] (3) 正規表示式 (二) 高階應用物件程式設計
- MyEclipse移動開發教程:遷移HTML5移動專案到PhoneGap(二)Eclipse移動開發HTML
- [.net 物件導向程式設計進階] (28) 結束語——告別2015物件程式設計
- 《Unity移動遊戲開發》讀後感Unity遊戲開發
- 移動遊戲開發的五個技巧遊戲開發
- HTML5遊戲開發進階 3 :物理引擎基礎HTML遊戲開發
- 《移動網頁設計與開發HTML5+CSS3+JavaScript》——1.7 測試,再測試,進行更多的測試網頁HTMLCSSS3JavaScript
- 移動web開發總結Web
- 移動端開發小結
- 移動開發之總結移動開發
- Rust 程式設計影片教程(進階)——027_3 高階特性 3Rust程式設計
- Qt程式設計獲取滑鼠移動事件QT程式設計事件
- 來黑馬程式設計師從零學前端與移動開發----移動web開發----伸縮佈局程式設計師前端移動開發Web
- 《jQuery移動開發》——2.1 語義HTML5jQuery移動開發HTML
- HTML5移動開發指南(筆記概要)HTML移動開發筆記
- PhoneJS - HTML5 JavaScript 移動開發框架JSHTMLJavaScript移動開發框架
- 移動設計八原則
- Rust 程式設計視訊教程(進階)——027_3 高階特性 3Rust程式設計
- 遷移HTML5移動專案到PhoneGapHTML
- web移動開發總結(六)Web移動開發
- 《BREW進階與精通——3G移動增值業務的運營、定製與開發》連載之6---移動增值業務概述
- 3D 移動3D
- 【效能測試】移動測試md知識總結第1篇:移動端測試課程介紹【附程式碼文件】
- 《移動網頁設計與開發HTML5+CSS3+JavaScript》——1.3 瞭解什麼是HTML5網頁HTMLCSSS3JavaScript
- HTML5在移動開發中的現狀HTML移動開發