本次Html5小游戏使用createjs来开发。基本涵盖这个游戏的全部核心代码。
如下是主要用到的元素:
- createjs.SpriteSheet
创建雪碧图,将生成好的雪碧图读取出来 - createjs.Container
创建容器,容器可以叠加,将多个容器加到一个容器,则操作一个容器就行,类似分组处理。 - createjs.Shape
创建矢量图形,创建的容器对象,可以设置hitArea=new createjs.Shape(),然后再在容器上设置点击或者移动事件。 - createjs.Bitmap
创建图形对象容器,new createjs.Bitmap()[x|y],x和y设置图片位置 - createjs.Sound
播放声音,播放前需要加载,new createjs.LoadQueue().installPlugin(createjs.Sound)注册声音插件.
也可以单独注册资源:createjs.Sound.registerSound(d.src, d.id);
然后播放对应注册资源的音频文件id即可:createjs.Sound.play(id);
创建舞台(stage),然后将创建的各种雪碧图,背景图,容器,全部加入到舞台,设置在舞台的位置(都是绝对位置,左上角为 0,0),this.stage.update(e);刷新舞台。
一、初始化
首先要初始化页面场景,和一些默认页面元素。
- 获取屏幕宽度,设置canvas
this.windowWidth = document.documentElement.clientWidth;
this.windowHeight = document.documentElement.clientHeight;
const canvas = document.getElementById('a_game');
canvas.setAttribute('width', 750);
canvas.setAttribute('height', (this.windowHeight * 750) / this.windowWidth);
- 设置场景
this.stage = new createjs.Stage(canvas);
this.stageWidth = canvas.width;
this.stageHeight = canvas.height;
createjs.Touch.enable(this.stage);
this.complete = complete || function () {};
this.musicChange = musicChange || function () {};
createjs.Ticker.paused = 0;
createjs.Ticker.addEventListener('tick', this.tick.bind(this));
createjs.Ticker.framerate = 60;
- 检测横屏竖屏
this.detectOrient();//首次先执行一次
window.onresize = this.debounce(this.detectOrient.bind(this), 300);// 检测窗口改变
Avoiding.prototype.detectOrient = function () {
const orient = document.getElementById('a_orient');
const cw = this.windowWidth;
const sw = window.screen.width;
if (cw == sw) {
// 竖屏
orient.className = 'orient_tips hide';
this.orient = 1;
return true;
}
// 横屏
orient.classList.remove('hide');
this.orient = 0;
return false;
};
如果设置了orient_tips,则提示用户改成竖屏
- 设置页面元素的缩放
let scale;
if (this.windowWidth <= 320) {
scale = 0.8;
} else if (this.windowWidth <= 360) {
scale = 1;
} else if (this.windowWidth <= 420) {
scale = 1.1;
} else if (this.windowWidth <= 736) {
scale = 1.15;
} else {
scale = 1.2;
- 创建背景图片
// 创建背景
Avoiding.fn.createBackground = function () {
this.bgContainer1 = new createjs.Bitmap(this.resource.bg);
this.bgContainer2 = this.bgContainer1.clone();
this.bgHeight = (2668 * this.stageWidth) / 750;
this.bgContainer1.y = -(this.bgHeight - this.stageHeight);
this.bgContainer2.y = this.bgContainer1.y - this.bgHeight;
this.stage.addChild(this.bgContainer1);
this.stage.addChild(this.bgContainer2);
};
- 处理雪碧图
Avoiding.fn.createSprite = function () {
this.spriteImages = new createjs.SpriteSheet({
images: [this.resource.sprite],
frames: [
[0, 0, 218, 176],
[218, 0, 218, 176],
[436, 0, 218, 176],
[0, 176, 148, 148],
[148, 176, 148, 148],
[296, 176, 148, 148],
[444, 176, 148, 148],
[0, 324, 148, 148],
[148, 324, 148, 148],
[296, 324, 148, 148],
[444, 324, 148, 148],
[0, 472, 121, 82],
[121, 472, 121, 81]
],
animations: {
normal: {
frames: [0]
},
giddy: {
frames: [1]
},
eat: {
frames: [2]
},
one: {
frames: [3]
},
two: {
frames: [4]
},
three: {
frames: [5]
},
four: {
frames: [6]
},
five: {
frames: [7]
},
six: {
frames: [8]
},
seven: {
frames: [9]
},
eight: {
frames: [10]
},
rock1: {
frames: [11]
},
rock2: {
frames: [12]
}
}
});
const self = this;
function create(obj, key) {
obj[key] = new createjs.Sprite(self.spriteImages, key);
}
this.brands.map((d) => {
create(self.brandObj, d);
});
this.rocks.map((d) => {
create(self.rockObj, d);
});
const brandGif = new createjs.SpriteSheet({
images: [this.resource.brandGif],
frames: {
width: 168,
height: 168
},
animations: {
gif: {
frames: [1, 2, 3, 4, 5],
speed: 0.2
}
}
});
this.brandGif = new createjs.Sprite(brandGif, 'gif');
};
- 创建操作的任务图像
/**
* 创建圈妹
*/
Avoiding.fn.createQuanmei = function () {
// 普通小圈妹妹
this.quanNormal = new createjs.Sprite(this.spriteImages, 'normal');
this.quanNormal.scaleX = this.quanScale;
this.quanNormal.scaleY = this.quanScale;
this.quanNormal.alpha = 1;
// 晕了的小圈妹妹
this.quanGiddy = new createjs.Sprite(this.spriteImages, 'giddy');
this.quanGiddy.scaleX = this.quanScale;
this.quanGiddy.scaleY = this.quanScale;
const giddyQuan = new createjs.SpriteSheet({
images: [this.resource.quanGif],
frames: {
width: 218,
height: 176
},
animations: {
giddy: {
frames: [1, 2, 3, 4, 5],
speed: 0.2
}
}
});
this.quanGiddyGif = new createjs.Sprite(giddyQuan, 'giddy');
this.quanGiddyGif.scaleX = this.quanScale;
this.quanGiddyGif.scaleY = this.quanScale;
const quanGiddC = new createjs.Container();
quanGiddC.addChild(this.quanGiddy);
quanGiddC.addChild(this.quanGiddyGif);
this.quanGiddC = quanGiddC;
this.quanGiddC.alpha = 0;
// 吃到东西的小圈妹妹
this.quanEat = new createjs.Sprite(this.spriteImages, 'eat');
this.quanEat.scaleX = this.quanScale;
this.quanEat.scaleY = this.quanScale;
this.quanEat.alpha = 0;
const quanC = new createjs.Container();
// 创建一个container,里面附加人物的不停变换效果
quanC.addChild(this.quanNormal);
quanC.addChild(this.quanGiddC);
quanC.addChild(this.quanEat);
quanC.width = 218 * this.quanScale;
quanC.height = 176 * this.quanScale;
quanC.x = (this.stageWidth - quanC.width) / 2;
quanC.y = this.stageHeight - quanC.height;
this.quanC = quanC;
//如果需要捕捉用户的事件点击,需要创建一个shape,由于产品需要触摸点是整个屏幕,所以把宽度调大了
const hitArea = new createjs.Shape();
hitArea.graphics.beginFill('#fff').drawRect(0, 0, this.stageWidth*2, this.stageHeight*2); // 这里是图片大小
this.quanNormal.hitArea = hitArea;
this.quanNormal.addEventListener('mousedown', this.touchStart.bind(this));
this.quanNormal.addEventListener('pressmove', this.touchMove.bind(this));
this.stage.addChild(quanC);
};
二、开始游戏
- 手势事件
Avoiding.fn.touchStart = function (e) {
this.startX = e.stageX;
this.startY = e.stageY;
this.startGame = 1;
};
Avoiding.fn.touchMove = function (e) {
if (this.quanGiddC.alpha) return;//如果是撞击了石头等,则不移动,晕1s
const { stageX, stageY } = e;
this.distanceX += stageX - this.startX;
this.distanceY += stageY - this.startY;
this.startX = stageX;
this.startY = stageY;
};
- 处理场景更新
Avoiding.fn.tick = function (e) {
if (e.paused === 0) {
if (this.startGame) {
this.movePeople();
this.moveObstacles();
this.createObstacles();
this.hitCheck();
this.updateScore();
}
// 刷新舞台
this.stage.update(e);
}
};
- 处理人物更新
移动人物,设置边界
Avoiding.fn.movePeople = function (e) {
if (!this.startX || !this.startY || !this.distanceX || !this.distanceY) { return; }
const x = this.detectX(this.quanC.x + this.distanceX, 176 * this.quanScale);
const y = this.detectY(this.quanC.y + this.distanceY, 218 * this.quanScale);
this.distanceY = 0;
this.distanceX = 0;
this.quanC.x = x;
this.quanC.y = y;
};
- 下滑障碍物和背景图片,给用户是人向前的感觉
背景图设置两个,同一个图片,做无限循环
/**
* 下滑障碍物
*/
Avoiding.fn.moveObstacles = function () {
const now = Date.now();
if (!this.moveLastTime) return (this.moveLastTime = now);
let diff = now - this.moveLastTime;
if (diff > 20) diff = 20;
const distance = diff * 0.1 * this.speed;
let y1 = this.bgContainer1.y;
let y2 = this.bgContainer2.y;
if (y1 >= this.stageHeight) {
y1 = y2 - this.bgHeight;
}
if (y2 >= this.stageHeight) {
y2 = y1 - this.bgHeight;
}
y1 += distance;
y2 += distance;
this.bgContainer1.y = y1;
this.bgContainer2.y = y2;
this.obstaclesList = this.obstaclesList.filter((d) => {
d.y += distance;
if (d.y > this.stageHeight || d.isHited) {
// d.x = -500;
this.obstaclesC.removeChild(d);
return false;
}
return true;
});
this.moveLastTime = Date.now();
};
- 障碍物和背景向下掉落,那么需要创建新的障碍物
这里设定了没滑动0.2个屏幕高度,则创建一个障碍物,且是出现五个积分物件再出现一个石头
Avoiding.fn.createObstacles = function () {
if (!this.moveDistance) return (this.moveDistance = this.bgContainer1.y);
const diff = this.bgContainer1.y - this.moveDistance;
if (Math.abs(diff) >= 0.2 * this.stageHeight) {
if (this.brandCount < 5) {
const d = this.getRandomBrand();
this.obstaclesList.push(d);
this.obstaclesC.addChild(d);
this.brandCount++;
if (this.brandCount == 5) {
const r = this.getRandomRock();
this.obstaclesList.push(r);
this.obstaclesC.addChild(r);
this.brandCount = 0;
}
}
this.moveDistance = this.bgContainer1.y;
}
};
- 检测撞击
撞击很容易理解,任务四周和掉落的东西的四周碰撞即表明是撞击了,此时将该物体表面是已撞击,在移动物体的时候,直接干掉这个remove物体。撞击了石头要眩晕,撞击物品要加积分
/**
* 检测撞击
*/
Avoiding.fn.hitCheck = function () {
const quanC = this.quanC;
const qx = quanC.x + 15;
const qy = quanC.y + 15;
const qw = quanC.width - 30;
const qh = quanC.height - 30;
const hitBrand = []; // 创建的泡泡列表
this.obstaclesList.map((d) => {
const {
x, y, width, height, isHited
} = d;
if (isHited) return;
if (x + width > qx && x < qx + qw && y + height > qy && y < qy + qh) {
if (d.type == 'brand') {
hitBrand.push(this.hitBrand(x, y));
} else {
this.hitRock();
}
d.isHited = true;
}
});
if (hitBrand.length == 0) return;
hitBrand.map((hb) => {
this.obstaclesC.addChild(hb);
});
};
总结:还没做的时候,觉得复杂,毕竟从来没搞过,其实还是很简单的。