仿跳一跳小游戏

文章目录

  • 一、游戏玩法介绍:
  • 二、新建项目:
        • 1. 新建 EUI 项目:
        • 2. 游戏场景分析:
        • 3. 加入图片资源。
  • 三、创建游戏开始场景:
        • 4. 分别在 resource 目录和 src 目录创建 scene 文件夹,并创建 EUI 组件 BeginScene:
        • 5. 游戏开始场景 BeginScene 的布局
  • 四、创建游戏场景界面:
        • 6. 创建 EUI 组件 GameScene.ts:
        • 7. 在 GameScene.exml 中实现布局:
        • 8. 创建场景控制器 SceneManager类,并把它设计成单例:
        • 9. 为游戏开始场景绑定点击方法,在 BeginScene.ts 中复制自定义粘贴开始按钮:
        • 10. 在 GameScene.ts 中复制自定义属性:
  • 五、小人跳跃轨迹:
        • 11. 运动轨迹的表示:
        • 12. 判断是否跳跃成功:
        • 13. 跳跃失败的游戏结束界面:

一、游戏玩法介绍:

  • 跳一跳使用手指按压屏幕蓄力;
  • 根据力量大小控制小 i 来跳跃;
  • 跳到下一个盒子上后积分累加;
  • 落到下一个盒子之外游戏结束。
    仿跳一跳小游戏_第1张图片

二、新建项目:

1. 新建 EUI 项目:

仿跳一跳小游戏_第2张图片

2. 游戏场景分析:
  • 开始场景:
    仿跳一跳小游戏_第3张图片

  • 游戏场景:
    仿跳一跳小游戏_第4张图片

  • 游戏结束场景:
    仿跳一跳小游戏_第5张图片

3. 加入图片资源。

三、创建游戏开始场景:

4. 分别在 resource 目录和 src 目录创建 scene 文件夹,并创建 EUI 组件 BeginScene:

仿跳一跳小游戏_第6张图片

5. 游戏开始场景 BeginScene 的布局

仿跳一跳小游戏_第7张图片

四、创建游戏场景界面:

6. 创建 EUI 组件 GameScene.ts:

仿跳一跳小游戏_第8张图片

7. 在 GameScene.exml 中实现布局:
  • image 组件设置背景图 bg_png,并填充上下左右;

  • 添加容器组件 Group,设置 ID:blockPanel,同样填充上下左右:
    仿跳一跳小游戏_第9张图片

  • 在容器内添加 Label 组件,这是 ID:scoreLabel;
    仿跳一跳小游戏_第10张图片

  • 在容器内添加 image 组件,这是 ID:player;
    仿跳一跳小游戏_第11张图片

8. 创建场景控制器 SceneManager类,并把它设计成单例:

仿跳一跳小游戏_第12张图片

// 场景控制单例 
private static shared: SceneManager;  

// 实例化单例获取方法 
public static Shared(): SceneManager {  
	if(!SceneManager.shared) {   
		SceneManager.shared = new SceneManager(); 
}  
return SceneManager.shared; 
}
  • 声明两个场景
// 开始场景 
private beginScene: BeginScene; 

// 游戏场景
private gameScene: GameScene;
  • 修改 SceneMangager 控制器为容器,以便于在 Main.ts 的 createGameScene 创建场景界面添加此控制器:
class SceneManager extends egret.Sprite{}
  • 在 Main.ts 的 createGameScene() 方法内添加场景控制器:
protected createGameScene(): void {        
	this.addChild(SceneManager.Shared());    
}
  • 在 SceneManger.ts 中实现初始化方法,实现数据初始化:
public constructor() {  
	super();  this.init(); 
}   

/**
* 初始化  
* */ 
private init() {  
	// 实例化两个场景  
	this.beginScene = new BeginScene();  
	this.gameScene = new GameScene();   

	// 默认添加开始场景  
	this.addChild(this.beginScene); 
}
  • 实现切换场景的方法:
// 切换场景 
public changeScene(type) {  
	if(type == 'gameScene') {   		
	this.beginScene.release();  
}  

// 移除所有显示列表中的对象  
this.removeChildren(); 

 // 添加下一个场景  
 this.addChild(this.gameScene); 
 }
  • 注意:在 BeginScene.ts 内,release 方法是用来解绑按钮点击事件:
// 移除事件 
public release() {
	if(this.beginBtn.hasEventListener(egret.TouchEvent.TOUCH_TAP)) {   
		this.beginBtn.removeEventListener(
			egret.TouchEvent.TOUCH_TAP,    
			this.tapHandler,    
			this);  
	} 
}
9. 为游戏开始场景绑定点击方法,在 BeginScene.ts 中复制自定义粘贴开始按钮:
// 开始按钮 
public beginBtn:eui.Button;
  • 为按钮绑定点击事件:
protected childrenCreated():void {  
	super.childrenCreated();   
	
	// 页面加载完毕后,调用自定义初始化方法  
	this.init(); 
}  

/** 
* 初始化方法(给开始按钮绑定点击事件) 
* */ 
private init() {  
	this.beginBtn.addEventListener(   
		egret.TouchEvent.TOUCH_TAP,   
		this.tapHandler,   
		this  
	); 
}  

// 点击按钮调用的方法 
private tapHandler() {  
	// 切换场景  
	SceneManager.Shared().changeScene('gameScene'); 
}
10. 在 GameScene.ts 中复制自定义属性:
// 游戏场景组 
public blockPanel:eui.Group; 

// 游戏场景中的积分 
public scoreLabel:eui.Label; 

// 小 i 
public player:eui.Image;
  • 使用工厂设计模式来创建盒子方块(回收 / 复用):
/**************************工厂设计模式*****************************/ 
// 所有方块资源的数组 
private blockSourceNames: Array<string> = []; 

// 所有方块的数组 
private blockArr: Array<eui.Image> = []; 

// 所有回收方块的数组 
private reBackBlockArr: Array<eui.Image> = [];
  • 工厂方法:
/** 
* 工厂方法,创建一个方块 
* */ 
private createBlock(): eui.Image {  
	// 定义一个方块盒子  
	var blockNode: eui.Image = null;
	if(this.reBackBlockArr.length) {   
		// 回收池里有,则直接取   
		blockNode = this.reBackBlockArr.splice(0,1)[0];  
	}else {   
		// 回收池里面没有,则重新创建  
		blockNode = new eui.Image();  
	}  

	// 使用随机背景  
	let n = Math.floor(Math.random() * this.blockSourceNames.length);  
	blockNode.source = this.blockSourceNames[n];  
	this.blockPanel.addChild(blockNode);   

	// 设置方块的锚点  
	blockNode.anchorOffsetX = 222;  
	blockNode.anchorOffsetY = 78;  
	
	// 把新建的 block 添加进入 blockArr 里  
	this.blockArr.push(blockNode);   
	return blockNode; 
}
  • 初始化方法,定义音频变量,在初始化方法里加载音频:
/**************************音频*****************************/ 
// 按下的音频 
private pushVoice: egret.Sound; 

// 弹跳的音频 
private jumpVoice: egret.Sound;
  • 绑定手指按下屏幕和放开的事件;
  • 设置玩家的锚点,选中底部圆心点的位置为锚点;
protected childrenCreated():void {  
	super.childrenCreated();  
	this.init(); 
}  

/**  
* 初始化  
* */ 
private init() {  
	this.blockSourceNames = ['block1_png','block2_png','block3_png'];   
	
	// 初始化音频  
	this.pushVoice = RES.getRes('push_mp3');  
	this.jumpVoice = RES.getRes('jump_mp3');   
	
	// 打开 touchEnabled 属性  
	this.blockPanel.touchEnabled = true; 
	this.blockPanel.addEventListener(   
		egret.TouchEvent.TOUCH_TAP,   
		this.onKeyDown,   
		this  
	);  

	this.blockPanel.addEventListener(   
		egret.TouchEvent.TOUCH_END,   
		this.onKeyUp,   
		this  
	);   
		
	// 设置玩家锚点  
	this.player.anchorOffsetX = this.player.width / 2;  
	this.player.anchorOffsetY = this.player.height - 20; 
}
  • 添加方向变量,往右添加是正值1,往左是负值-1:
// 下一个盒子方向(1 靠右侧出现 / -1 靠左侧出现) 
public direction: number = 1;
  • 重置游戏的方法:
// 重置游戏 
public reset() {  
	// 清空舞台  
	this.blockPanel.removeChildren();  
	this.blockArr = [];  
	
	// 添加一个方块  
	let blockNode = this.createBlock();  
	blockNode.touchEnabled = false;   
	
	// 设置方块的起始位置 
	blockNode.x = 200;  
	blockNode.y = this.height / 2 + blockNode.height;
	this.currentBlock = blockNode;   
	
	// 摆正小人的位置  
	this.player.x = this.currentBlock.x;  
	this.player.y = this.currentBlock.y;  
	this.blockPanel.addChild(this.player);   
	
	// 添加积分  
	this.blockPanel.addChild(this.scoreLabel);   
	
	// 重置盒子方向  
	this.direction = 1;  
	 
	// 添加下一块方块  
	this.addBlock();  
}

仿跳一跳小游戏_第13张图片

仿跳一跳小游戏_第14张图片
仿跳一跳小游戏_第15张图片
仿跳一跳小游戏_第16张图片

  • tan 的角度并声明落脚点
// tanθ 角度值 
public tanAngle: number = 0.55604719640118; 

// 落脚点 
private targetPos: egret.Point;
  • 添加方块盒子的方法:
/** 
* 添加一个方块 
* */ 
private addBlock() {  
	// 随机一个方块  
	let blockNode = this.createBlock();  
	
	// 设置位置  
	let distance = this.minDistance + Math.random()*(this.maxDistance - this.minDistance);     
	if(this.direction > 0) {   
		blockNode.x = this.currentBlock.x + distance;   
		blockNode.y = this.currentBlock.y - distance*this.tanAngle;  
	}else {   
		blockNode.x = this.currentBlock.x - distance;   
		blockNode.y = this.currentBlock.y - distance*this.tanAngle;  
	}  
	this.currentBlock = blockNode; 
}
  • 实现手指按下执行的方法:
/**  
* 按下的事件逻辑  
* */ 
private onKeyDown() { 
	// 播放按下的音频  
	this.pushSoundChannel = this.pushVoice.play(0,1);  
	
	// 变形  
	egret.Tween.get(this.player).to({   
		scaleY: 0.5  
	}, 3000);   
	this.isReadyJump = true; 
}
  • 实现手指松开的方法:停止按压
/**  
* 放开的事件逻辑  
* */ 
private onKeyUp() {  
	// 判断是否在按下状态  
	if(!this.isReadyJump) {   
		return;  
	}   
	
	// 声明落脚点坐标  
	if(!this.targetPos) {   
		this.targetPos = new egret.Point();  
	}   
	
	// 立即让屏幕不可点,等小人落下后重新可点  
	this.blockPanel.touchEnabled = false;     
	
	// 停止播放按压音频,并且播放跳跃音频  
	this.pushSoundChannel.stop();  
	this.jumpVoice.play(0,1);   
	
	// 清除所有动画  
	egret.Tween.removeAllTweens();  
	this.blockPanel.addChild(this.player);   
	
	// 结束跳跃状态  
	this.isReadyJump = false;   
	
	// 落脚点坐标  
	this.targetPos.x = this.player.x + this.jumpDistance * this.direction;  
	
	// 根据落点重新计算斜率,确保小人往目标中心跳跃  
	this.targetPos.y = this.player.y + this.jumpDistance * (this.currentBlock.y - this.player.y)/(this.currentBlock.x - this.player.x) * this.direction;    
	 
	// 执行跳跃动画  
	egret.Tween.get(this).to({factor: 1}, 500).call(() => {   
		this.player.scaleY = 1;   
		this.jumpDistance = 0;  
	});   
	
	// 执行小人空翻动画  
	this.player.anchorOffsetY = this.player.height / 2;  
	egret.Tween.get(this.player).to({rotation: this.direction > 0 ? 360 : -360}, 200).call(() => {   
		this.player.rotation = 0;  
		}).call(() => {   
			this.player.anchorOffsetY = this.player.height - 20;  
		}); 
	}

五、小人跳跃轨迹:

11. 运动轨迹的表示:

仿跳一跳小游戏_第17张图片

// 添加 factor 的 set,get 方法,注意使用 public  
public get factor(): number {  
	return 0; 
} 

// 计算方法参考,二次贝塞尔曲线公式 
public set factor(value: number) {  
	this.player.x = (1-value)*(1-value) * this.player.x +   2 * value * (1-value)*(this.player.x + this.targetPos.x) / 2 + value * value * (this.targetPos.x);
	this.player.y = (1-value)*(1-value) * this.player.y +   2 * value * (1-value)*(this.player.y - 300) / 2 + value * value * (this.targetPos.y); 
}

对应 onKeyUp方法里执行跳跃动画的变量 factro:

// 执行跳跃动画 
egret.Tween.get(this).to({factor: 1}, 500).call(() => {  …… });
12. 判断是否跳跃成功:
// 根据 this.jumpDistance 来判断跳跃是否成功 
private judgeResult(){     
	// pow 函数用于求幂      
	if(Math.pow(this.currentBlock.x - this.player.x, 2)   + Math.pow(this.currentBlock.y - this.player.y, 2) <= 70*70) {   
		// 跳跃成功   
		// 更新积分   
		this.score++;   
		this.scoreLabel.text = this.score.toString();  
		  
		// 随机下一个方块出现的位置   
		this.direction = Math.random() > 0.5 ? 1 : -1;    
		
		// 当前方块要移到相应的跳跃点的距离   
		var blockX, blockY;   
		blockX = this.direction > 0 ? this.leftOrigin.x : this.rightOrigin.x;   
		blockY = this.height / 2 + this.currentBlock.height;    
		
		// 小人要移动到的点   
		var playerX, playerY;   
		playerX = this.player.x - (this.currentBlock.x - blockX);   
		playerY = this.player.y - (this.currentBlock.y - blockY);    
		
		// 更新页面的位置   
		this.update(this.currentBlock.x - blockX, this.currentBlock.y - blockY);   
		 
		// 更新小人的位置   
		egret.Tween.get(this.player).to({    
			x: playerX,    
			y: playerY   
		}, 1000).call(() => {    
			// 开始创建下一块方块    
			this.addBlock();    
			
			// 让屏幕重新可点    
			this.blockPanel.touchEnabled = true;   
		})    
		console.log("x" + this.currentBlock.x);   
		}else {   
		// 跳跃失败   
		console.log("游戏失败!");   
		this.overPanel.visible = true;   
		this.overScoreLabel.text = this.score.toString();  
	} 
}
// 更新页面 
private update(x, y) {  
	egret.Tween.removeAllTweens();  
	for(var i: number = this.blockArr.length - 1; i >= 0; i--) {         
		var blockNode = this.blockArr[i];         
		if(blockNode.x + (blockNode.width - 222) < 0 ||        
			blockNode.x - 222 > this.width || blockNode.y - 78 > this.height) {
				// 方块超出屏幕,从显示列表中移除             
				this.blockPanel.removeChild(blockNode);   
				this.blockArr.splice(i,1);   
				
				// 添加到回收数组中   
				this.reBackBlockArr.push(blockNode);         
			}else {   
			// 没有超出屏幕的范围,则移动   
			egret.Tween.get(blockNode).to({    
				x: blockNode.x - x,    
				y: blockNode.y - y   
			}, 1000)         
		}     
	} 
}
  • 调用 judgeResult 来判断当前跳跃是否成功在 onKeyUp 方法里:
// 执行跳跃动画 
egret.Tween.get(this).to({factor: 1}, 500).call(() => {
     this.player.scaleY = 1;     
     this.jumpDistance = 0;     
     
     // 判断是否跳跃成功     
     this.judgeResult(); 
});
  • 导入第三方库(使用 egret build -a 命令导入):

  • 导入第三方库到工程文件目录中,将该文件夹下的内容拷贝到所创建的 egret 项目文件目录中,命名为 game
    仿跳一跳小游戏_第18张图片

  • 在项目中的 egretProperties.json 文件中加入 game 文件名和路径
    仿跳一跳小游戏_第19张图片

  • 在项目编辑器的终端输入 egret build -e 命令,等待编译成功

  • 使用第三方库 game 里的Ticker 心跳计时器,用来计时手指按下屏幕的时间长度,通过时间长度来计算跳跃的距离

// 心跳计时器 
egret.Ticker.getInstance().register(function(dt){  
	dt /= 1000;  
	if(this.isReadyJump) {
		this.jumpDistance += 300 * dt;  
	} 
}, this)
13. 跳跃失败的游戏结束界面:

仿跳一跳小游戏_第20张图片

你可能感兴趣的:(项目,白鹭引擎,游戏,typescript)