当前位置: 首页 > 工具软件 > Quintus > 使用案例 >

Quintus小游戏制作之Beta(二)

空英达
2023-12-01

        上一期讲了关于游戏初始化的一些设置,然而我们还不能真正在屏幕上看到任何东西。这一期我将会实现一个弹跳的方块,同时这期内容也将真正涉及到游戏的具体制作过程。


三、添加精灵

        万事具备,只欠东风。是时候添加我们的主人公小方块了。

Q.Sprite.extend 'Player',
		init: (p) !->
			@_super p,
				x: 100, y: 400
				w: 42, h: 42
				vx: 0, vy: 0
				ax: 0, ay: 0
				gravity: 0.02
				jumpable: false
				start: false
			@add '2d, tween'
			@on 'hit', @, @detect
		detect: (collision) !->
			if collision.obj.isA 'Ground'
				@p.vy = 0
				@p.jumpable = true
			else @die!
		pass: !->
			loadNextLevel!
		die: !->
			if !@p.hidden
				@p.hidden = true
				@p.vx = 0
				@animate { angle: 0 }, 0.2, Q.Easing.Linear, callback: !-> loadNextLevel!
		draw: (ctx) !->
			ctx.fillStyle = 'white'
			ctx.fillRect -@p.cx, -@p.cy, @p.w, @p.h
		step: (dt) !->
			@p.vx = 5.5
			@p.x += @p.vx
			@p.y += @p.vy
			if Q.inputs['up'] and @p.jumpable is true
				@p.jumpable = false
				@p.vy = -9.2
				@animate { angle: 180 + @p.angle }, 0.9
				Q.audio.play 'jump.mp3'

			if @p.y > groundY - @p.h/2 then @p.y = groundY - @p.h/2      # avoid falling bug
	
			if @p.x > @p.stage.options.w + @p.w / 2 then @pass!

        Q.Sprite.extend( name, options ) 利用Quintus的继承方法扩展一个Sprite类,第一个参数name是扩展类的名称,第二个参数是一个集合,里面可以重定义默认函数或者自定义函数。这里,init 方法、draw方法、step方法均是覆盖了原有默认方法。 init( p ) 方法初始化精灵的默认大小、位置、速度和加速度等性质,这些性质可以在实例化时被覆盖,另外init方法还可以给精灵添加一些Quintus组件,比如这里的"2d"、"tween",实现碰撞检测和渐进动画。 draw( ctx ) 接收一个回调的 canvas 对象, 其用法和canvas一样,这里只是画了一个白色方块。 step( dt ) 函数每一秒被调用60次, 可以用来实现方块的移动和按键判断。注意在step方法中代码不宜过于复杂耗时,不然游戏的效率会变得非常低下。

        另外,我还给小方块添加了一些自定义的方法。detect( collision ) 函数每当精灵发生碰撞时被调用,碰撞检测由组件'"2d"实现,具体细节可以忽略。我希望小方块在发生碰撞后判断对方是否为地面,如果是则允许再次跳跃,否则执行死亡方法。die() 函数将玩家方块隐藏起来并在0.2秒后重新加载当前关卡。win() 函数调用了自定义方法loadNextLevel(),代码会在下章讲解,作用是加载关卡。

        这里我们可以看到animate方法出现了两次,它属于Quintus的"tween"组件来执行渐进动画的,obj.animate( options, time, style, callback ) options参数对应于init方法,为动画结束时的精灵性质, time是动画的时间,默认为1秒,style决定渐变的方法,默认为线性渐变,callback是动画结束后的回调函数。后三个参数在使用时均可以省略。


四、添加场景

        光有精灵是不行的,精灵的展现需要有场景做支撑。

        我们需要考虑好游戏的场景布局,通常我喜欢把游戏分3层:背景层、逻辑层、UI层。每一层其实就是Quintus场景的一个Scene实例。以下是背景层和UI层的代码实现。

Q.scene 'background', (stage) !->
	stage.insert new Q.Repeater { asset: 'background.png' }

Q.scene 'UI', (stage) !->
	button = new Q.UI.Button { x: stage.options.w/8*7, y: 150, fill: 'white', label: 'Pause', font: '400 24px Pico', fontColor: 'rgb(167,34,243)' }
	button.on 'click', !->
		if Q.loop isnt null then Q.pauseGame! else Q.unpauseGame!
	stage.insert button

        Q.scene( name, function(stage){...} ) 这里name参数是创建场景的名字,function(stage){...} 接收一个stage参数,它是Quintus的舞台,场景便是通过Quintus舞台来展现的,可以把场景想象成舞台上的背景道具和演员,而这里的函数通过stage参数告诉舞台,场景需要什么。比如,背景场景background通过 stage.insert 函数将平铺图"background.png"添加到了舞台中,也即是,每当Quintus舞台展现background场景时,我们就会看到“background.png”图片平铺整个屏幕。“background.png”图片会从先前自定义的图片路径下读取。

        同理,UI层主要实现了一个暂停按钮,通过 Q.UI.Button 实例化出button, 并对button重写了 "click" 点击事件的执行函数。这里,利用Quintus的Q.loop 可以判断游戏是否在运行,利用Q.pauseGame() 暂停游戏,利用Q.unpauseGame() 恢复游戏。值得注意的是,暂停游戏并不会关闭游戏音效,Quintus在这一点上并没有实现关于声音的暂停和恢复。

        现在我们便可以将小方块加载到屏幕上来了。

Q.scene 'level', (stage) !->
	stage.insert new Q.Ground { w: stage.options.w, x: stage.options.w/2 }
	player = stage.insert new Q.Player { stage: stage }
        Q.Player和Q.Ground是我们扩展的Sprite类,Q.Ground在前面忽略了,它利用draw方法画了一条屏幕宽度的白线,并垂直居中于屏幕。通过stage.insert将两者加载到逻辑层"level"中来,当舞台展现“level”时便可以看见小方块了。

        现在终于可以看到loadReady函数了

	loadReady = !->
		$ '#loading' .hide!
		Q.stageScene 'background', 0
		Q.stageScene 'UI', 2
		loadNextLevel!
        没错,通过Q.stageScene展现场景,第二个参数是场景的渲染层次,越低越容易被遮蔽。在loadNextLevel函数中调用Q.stageScene("level",1)便能看见小方块了。

	loadNextLevel = !->
		Q.clearStage 1
		if currentLevel! > maxLevel!
			Q.clearStage 2
			Q.stageScene 'win', 1
		else
			Q.stageScene 'level', 1


 类似资料: