最近小游戏做完了,记录一下。开发工具cocos creator,纯前端。
游戏介绍
小游戏一共只有两个游戏场景,分别是开始界面和游戏界面。
开始界面
游戏界面
游戏实现
-
游戏背景无限滚动(用两张背景图交替往上滚动,实现游戏背景一直在动)
-
自动生成新的阶梯以及小球和阶梯的碰撞事件
-
屏幕触碰事件(控制小球左右移动)
-
小球每下降3000像素(没搞懂单位是什么),分数加一
游戏制作
一、游戏背景无限滚动
背景滚动主要靠两张背景图片实现,就是轮流往上移动。
bg节点下挂了两个背景,通过给bg节点挂个脚本,让back1和back2一直往上移动,当移动到一定距离后就把上面的图片移动到下面,从而实现背景无限滚动。
主要实现滚动代码如下:
1 2 3 4 5 6 7 8 9 10 11 12
|
update(deltaTime: number) { let n = this.node.parent.getComponent(main).n; this.back1.setPosition(0,this.back1.position.y+1+n,0); this.back2.setPosition(0,this.back2.position.y+1+n,0); console.log(this.back1.position); if (this.back1.position.y>=3000){ this.back1.position = new Vec3(0,-3000,0); } if(this.back2.position.y>=3000){ this.back2.position = new Vec3(0,-3000,0); } }
|
二、自动生成新的阶梯以及小球和阶梯的碰撞事件
生成阶梯通过主脚本控制,游戏中有多种阶梯,我为每一种阶梯分别写了一个脚本(应该可以公用一个,懒得改了)。即有一个公用的脚本控制不同阶梯的生成,每一个阶梯都有自己专有的脚本来控制关于自身的事件。
游戏主脚本控制随机生成阶梯代码:
1 2 3 4 5 6
|
floors:Prefab[] = []; generalfloor(){ let floor = instantiate(this.floors[Math.floor(Math.random()*(this.floors.length-1))]); floor.parent = this.node; floor.position = new Vec3(randomRange(-380,380),-1000,0); }
|
这种是最普通的阶梯,只需要随着背景一直滚动即可。
主要实现代码:
1 2 3 4 5 6 7 8 9 10
|
update(deltaTime: number) { let self = this; let n = this.node.parent.getComponent(main).n; this.node.position = new Vec3(this.node.position.x,this.node.position.y+1+n,0); if (this.node.position.y>=1000){ setTimeout(()=>{ self.node.destroy(); },0); } }
|
这种比第一种特殊一点,需要实现碰撞后会由原本实心的阶梯变成半透明的阶梯,再消失。整个从实心到消失的时间设置的是三秒。
- 首先需要我们制作阶梯由实心到半透明的动画
- 之后节点注册碰撞函数,监听碰撞信息,当碰撞后就调用动画,让它变半透明
- 最后我们删除节点,实现阶梯消失
制作动画自己百度吧,两秒就能学会,直接看代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
|
start() { let collider = this.getComponent(Collider2D); if (collider) { collider.on(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this); } } onBeginContact(selfCollider: Collider2D, otherCollider: Collider2D, contact: IPhysics2DContact null) {
if(otherCollider.node.name = "ball"){ this.getComponent(Animation).play(); setTimeout(()=>{ if(this.node){ this.node.active = false; } },3000) } }
update(deltaTime: number) { let self = this; let n = this.node.parent.getComponent(main).n; this.node.position = new Vec3(this.node.position.x, this.node.position.y + 1 + n, 0); if (this.node.position.y >= 1000) { setTimeout(() => { self.node.destroy(); }, 0); } }
|
这里最大的坑就是this.node.active = false;//三秒后让节点失效,一开始没考虑全面,想着动画运行完直接删除节点得了。自己测试中发现,有时候会调用destroy失败,节点不存在。
是因为没考虑到当碰撞后,还没到三秒删除节点的时间,此时节点的高度超过1000,在updata()函数里已经把节点删除了,再等到三秒后节点就不存在了,自然删除失败。
这一种阶梯看起来像弹簧一样,可以实现跳跃功能的。主要是碰撞后给小球一个向上的速度,水平速度不变。
主要代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
onBeginContact(){ this.donghua.play(); this.ball.getComponent(RigidBody2D).linearVelocity = new Vec2(this.ball.getComponent(RigidBody2D).linearVelocity.x,20); }
update(deltaTime: number) { let self = this; let n = this.node.parent.getComponent(main).n; this.node.position = new Vec3(this.node.position.x, this.node.position.y + 1+n, 0); if (this.node.position.y >= 1000) { setTimeout(() => { self.node.destroy(); }, 0); } }
|
这种阶梯一碰就game over,会调用游戏的暂停函数,然后弹出来页面让你选择复活还是回到首页。
主要代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
onBeginContact(selfCollider: Collider2D, otherCollider: Collider2D, contact: IPhysics2DContact null) {
if(otherCollider.node.name == "ball"){ this.node.parent.getComponent(main).stopgame(); } }
update(deltaTime: number) { let self = this; let n = this.node.parent.getComponent(main).n; this.node.position = new Vec3(this.node.position.x, this.node.position.y + 1 + n, 0); if (this.node.position.y >= 1000) { setTimeout(() => { self.node.destroy(); }, 0); } }
|
1 2 3 4 5 6
|
stopgame(){ let alert = instantiate(this.floors[6]); alert.setPosition(new Vec3(0,0,0)); alert.parent = this.node; director.pause(); }
|
弹窗有两个按钮复活和首页。
复活按钮实现:
1 2 3 4 5 6 7
|
click(button:Button){ director.resume(); let ball = this.node.parent.parent.getChildByName("ball"); ball.setPosition(new Vec3(0,300,0)); ball.getComponent(RigidBody2D).linearVelocity = new Vec2(0,0); this.node.parent.destroy(); }
|
回到首页按钮实现:
1 2 3 4
|
home(button: Button) { director.loadScene("start"); this.node.parent.destroy(); }
|
这个阶梯是向右移动阶梯,碰撞过程中会让小球向右速度每帧加0.2。
主要代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
onPreSolve(selfCollider: Collider2D, otherCollider: Collider2D, contact: IPhysics2DContact null) { let x = otherCollider.getComponent(RigidBody2D).linearVelocity.x; otherCollider.getComponent(RigidBody2D).linearVelocity = new Vec2(x+0.2,0); } update(deltaTime: number) { let self = this; let n = this.node.parent.getComponent(main).n; this.node.position = new Vec3(this.node.position.x, this.node.position.y + 1+n, 0); if (this.node.position.y >= 1000) { setTimeout(() => { self.node.destroy(); }, 0); } }
|
这个阶梯是向左移动阶梯,碰撞过程中会让小球向左速度每帧加0.2。
主要代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
onPreSolve(selfCollider: Collider2D, otherCollider: Collider2D, contact: IPhysics2DContact null) { let x = otherCollider.getComponent(RigidBody2D).linearVelocity.x; otherCollider.getComponent(RigidBody2D).linearVelocity = new Vec2(x - 0.2, 0);
} update(deltaTime: number) { let self = this; let n = this.node.parent.getComponent(main).n; this.node.position = new Vec3(this.node.position.x, this.node.position.y + 1+n, 0); if (this.node.position.y >= 1000) { setTimeout(() => { self.node.destroy(); }, 0); } }
|
最后是游戏最上面的尖刺和小球掉到最底下从屏幕消失,二者都会触发游戏结束。
主要代码实现:
1 2 3 4 5
|
onBeginContact(selfCollider: Collider2D, otherCollider: Collider2D, contact: IPhysics2DContact null) { if(otherCollider.node.name == "ball"){ this.node.parent.getComponent(main).stopgame(); } }
|
1 2 3
|
if (this.ball.position.y <= -1000) { this.stopgame(); }
|
三、屏幕触碰事件
游戏主要通过触碰实现小球的向左,向右移动。实现方式很简单,注册监听函数,判断当前触摸点在左边还是右边即可。
主要代码实现:
1 2 3 4 5 6 7 8 9 10 11 12
|
movestart(touch:EventTouch){ let position = touch.getUILocation(); let x = this.node.getComponent(UITransform).convertToNodeSpaceAR(new Vec3(position.x,position.y,0)).x; if(x>0){ this.ball.getComponent(RigidBody2D).linearVelocity = new Vec2(20, this.ball.getComponent(RigidBody2D).linearVelocity.y); }else{ this.ball.getComponent(RigidBody2D).linearVelocity = new Vec2(-20, this.ball.getComponent(RigidBody2D).linearVelocity.y); } } moveend(touch:EventTouch){ this.ball.getComponent(RigidBody2D).linearVelocity = new Vec2(0, this.ball.getComponent(RigidBody2D).linearVelocity.y); }
|
四、分数实现
用一个lable标签记录当前的分数。
主要代码实现:
1 2 3 4 5
|
update(deltaTime: number) { this.score+=3; let x = this.score/3000; this.getComponent(Label).string = Math.floor(x).toString(); }
|
计算方式自己看吧,我也不知道具体怎么算的,反正这样写分数增加的很慢(纯要求难度)。
总结
第一次做出来游戏,挺有成就感的(虽然游戏素材都是copy别人的,游戏玩法也是抄袭的,但是整个游戏代码和框架是我自己做的,这也很强了有没有)。
第一次做,大部分场景的实现还是靠百度的,虽然有官方的使用文档但一开始也看不懂好吧。
一回生二回熟,下次做一个带后端的,纯前端唯一的好处就是反应快,不过微信小游戏发布最大只能4m,这一次差一点就超了。
小游戏还在审核过程中,没发布,项目开源到我的github上了,感兴趣的可以看看。
github地址:https://github.com/nibabashilkk/100-floors-down
电脑没装git,只能传个压缩包了。