微信跳一跳项目总结
一、UGUI
1.开始游戏
提示Tip(text)
得分Score(text)
2.结束游戏
最终得分FinalScore(text)
游戏结束 GameOver(text)
重新开始按钮RestartBtn(button)
RestartButton.onClick.AddListener(() => { SceneManager.LoadScene(0); });
广告弹窗 含两个按钮
二、脚本控制
游戏体量不大这里用了一个脚本实现所有功能
1.生成台阶
首先拿到预制体 声明一个数组Boxtemplates[ ] 里面存放圆形台阶和方形台阶
然后克隆上面的台阶库,并设定位置、大小、颜色
prefab=Boxtemplates[ Random.range(0,Boxtemplates.Length) ]
NextStage=GameObject.Instantiate(prefab).transform
2.鼠标控制
首先MouseButtonDown 记录鼠标按下开始时间 用于后面计算跳跃力度
然后 GetMouseButton中改变玩家和台阶的Scale 实现蓄力效果
最后GetMouseButtonUp中 运行 跳跃功能函数方法 运用DOTween插件
Bug 1
玩家在空中还是可以点击鼠标进行跳跃
解决方法:设置一个bool变量EnableInput 再触发碰撞后才为true 一开始为false 并且 松开鼠标变为false 就可以解决
Bug2
通过试玩发现 玩家飞在空中是点击鼠标不放 落在台阶后松开鼠标 玩家会弹飞 这里是 落在台阶EnableInput为true 时 鼠标状态为Input.GetMouseButton 跳过了Input.GetMouseDown这一环节 使StartTime 还为上一条的StartTime 导致 elapse>1
解决方法:类似于Bug1 增加一个bool变量 isMouseDown 只有Input.GetMouseButtonDown 后才能获取MouseButton MouseButtonUp的操作。 一开始isMouseDown为false 只有Input.GetMouseButtonDown操作后为true 后面在MouseButtonUp变为false 即可解决。
增加一个判断条件
if (Input.GetMouseButton(0)&&isMouse==true)
if (Input.GetMouseButtonUp(0)&&isMouse==true)
3.跳跃功能
确定方向
用到了生成台阶方法中NextStage的Pos 两个位置相减并且normalized 得到方向 为了减少误差可以让Y=0 最后用DOTween插件实现玩家跳跃翻转的效果 这里注意下normalized的误差 有的时候会0.9几 所以设置判定条件的时候不要写 1
计算力度
Rig.AddForce(new Vector3(0, 5f, 0) + jumpDirection * elapse * Force, ForceMode.Impulse);
AddForce的常用API 一个表示xyz三个方向力度的vec3 和力的模式
4.OnCollisionEnter
判断是否落在台阶上
给台阶预制体创建一个子物体RolesPos 将玩家的位置赋给子物体 再用子物体的localposition 判断玩家落在了台阶的什么位置 从而确定是否成功落在台阶上
成功落在台阶上则 生成台阶 增加分数 移动相机 使EnaleInput 为true
5.增加分数AddScore
首先获取玩家人物位置 然后计算人物位置与台阶的位置(中心)的距离
targetDistance = Vector3.Distance(hitPos, CurrentStage.transform.position)
实现双倍得分功能
最后显示再UI界面上 ScoreText=“得分”+Score 这里会自动转换成string 显示在ui界面上
6.MoveCamera
这里使用DOTween插件
Camera.main.transform.DOMove(transform.position + CameraRelativePosition, 1);
最后附上源码
using DG.Tweening;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class Player : MonoBehaviour
{
//添加的力
private float Force = 4;
//最大距离,指的是盒子生成的最大距离
private float MaxDistance=2f;
//第一个盒子
public GameObject Stage;
//盒子仓库
public GameObject[] BoxTemplates;
//右上角的得分文本
public Text ScoreText;
//人物身上的粒子效果
public GameObject Particle;
//人物的头部和身体的Transform
public Transform Head;
public Transform Body;
//游戏结束的时候,提示游戏结束的文本
public GameObject GameOver;
//游戏结束的时候显示的分数面板
public Text FinalScore;
//重新开始游戏的按钮
public Button RestartButton;
//角色身上的音频源组件
public AudioSource audioSource;
// 成功跳到盒子上面的音效
//蓄力的音效
//开始游戏的音效
//失败的音效
public AudioClip SuccessAudio, AddForceAudio, StartAudio, FallAudio;
//角色身上的刚体组件
public Rigidbody Rig;
//开始加力的时间,
private float StartAddForceTime;
//当前的盒子
private GameObject CurrentStage;
//相机相对的位置
private Vector3 CameraRelativePosition;
//本局的分数统计
public int Score;
//盒子生成的方向数组
Vector3[] Directions = new Vector3[] { new Vector3(1,0,0), new Vector3(0, 0, 1) };
//启用输入与否,也就是限制当前能否控制玩家用的
private bool EnableInput = false;
//舞台的初始位置
private Vector3 StageInitPos;
private Vector3 StageInitScale;
//上次奖励的分数
private int LastReward=1;
//下一个盒子
private Transform NextStage;
GoogleAdMob googleAdMob;//谷歌广告的脚本
private Button LookBtn;//观看和取消的按钮
private Button CancelBtn;
private Transform AdMobTips;//提示的窗口
private GameObject gooleAdMobGO;
public GameObject gooleAdMobPrefabs;
public Text LoveText;
private bool isMouse=false;
void Start()
{
CurrentStage = Stage;
//相机的位置减去人物的位置,获得他们的相对距离
CameraRelativePosition = Camera.main.transform.position - transform.position;
StageInitPos = Stage.transform.localPosition;
StageInitScale = Stage.transform.localScale;
//点击重新开始按钮,重新加载一次场景
RestartButton.onClick.AddListener(() => { SceneManager.LoadScene(0); });
Rig.centerOfMass = Vector3.zero;
SpawnStage();//生成出舞台
//播放开始游戏的音效
PlayAudio(StartAudio);
gooleAdMobGO = GameObject.Find("GoogleAdMob");//查询谷歌广告脚本的物体
if (gooleAdMobGO == null)
{
gooleAdMobGO = GameObject.Instantiate(gooleAdMobPrefabs);
gooleAdMobGO.name = "GoogleAdMob";
}
googleAdMob = gooleAdMobGO.GetComponent<GoogleAdMob>();
AdMobTips = GameObject.Find("Canvas").transform.Find("AdMobTips").transform;
LookBtn = AdMobTips.transform.Find("LookBtn").GetComponent<Button>();
CancelBtn = AdMobTips.transform.Find("CancelBtn").GetComponent<Button>();
LookBtn.onClick.AddListener(googleAdMob.PlayAdsense);
CancelBtn.onClick.AddListener(OnGameOver);
}
/// <summary>
/// 生成舞台
/// </summary>
private void SpawnStage()
{
//从仓库中随机取出一个舞台
GameObject prefab = BoxTemplates[Random.Range(0, BoxTemplates.Length)];
NextStage = GameObject.Instantiate(prefab).transform;
//随机设置在上一个盒子的上方或者右边
NextStage.position = CurrentStage.transform.position + Directions[Random.Range(0, Directions.Length)] * Random.Range(1.2f, MaxDistance);
var randomScale = Random.Range(0.5f, 1);
//随机设置缩放
NextStage.transform.localScale = new Vector3(randomScale, 0.5f, randomScale);
//随机设置颜色
NextStage.GetComponent<MeshRenderer>().material.color =
new Color(Random.Range(0f, 1f), Random.Range(0f, 1f), Random.Range(0f, 1f));
}
// Update is called once per frame
void Update()
{
Rig.WakeUp();//避免刚体自动休眠
if (EnableInput)
{
if (Input.GetMouseButtonDown(0))
{ isMouse = true;
//当允许人物控制且鼠标左键被按下的时候记录开始蓄力的时间
StartAddForceTime = Time.time;
Particle.SetActive(true);
//播放音效
PlayAudio(AddForceAudio);
}
if (Input.GetMouseButton(0)&&isMouse==true)
{
//鼠标在按住的期间要把舞台往下移动,且缩小人物Y轴的缩放
//限制缩小的最大限度
if (CurrentStage.transform.localScale.y>0.4f)
{
//身体缩小
Body.transform.localScale += new Vector3(1.5f, -1f, 1.5f) * 0.05f * Time.deltaTime;
//随着身体缩小,头部也要往下移动
Head.transform.localPosition += new Vector3(0, -1, 0) * 0.1f * Time.deltaTime;
//盒子位置和缩放调整
CurrentStage.transform.localScale += new Vector3(0, -1, 0) * 0.15f * Time.deltaTime;
CurrentStage.transform.localPosition += new Vector3(0, -1, 0) * 0.15f * Time.deltaTime;
}
}
if (Input.GetMouseButtonUp(0)&&isMouse==true)
{
isMouse = false;
//放开鼠标左键的时候
audioSource.Stop();
//获取到总共蓄力了多少时间
var elapse = Time.time - StartAddForceTime;
Debug.Log(elapse);
if (elapse > 1)
{
//添加限制,最多蓄力一秒,
elapse = 1;
}
//关闭蓄力的粒子
Particle.SetActive(false);
//起跳
OnJump(elapse);
//还原人物形状
Body.transform.DOScale(0.1f, 0.2f);
Head.transform.DOLocalMoveY(0.29f, 0.2f);
//还原盒子的形状
CurrentStage.transform.DOLocalMoveY(StageInitPos.y, 0.4f);
CurrentStage.transform.DOScaleY(StageInitScale.y, 0.4f);
EnableInput = false;
}
}
}
/// <summary>
/// 进入跳跃
/// </summary>
/// <param name="elapse"></param>
private void OnJump(float elapse)
{
//通过目标位置-人物位置 求出跳跃的方向
Vector3 jumpDirection = (NextStage.position - transform.position).normalized;
jumpDirection.y = 0;
Debug.Log(jumpDirection);
//给刚体加力
Rig.AddForce(new Vector3(0, 5f, 0) + jumpDirection * elapse * Force, ForceMode.Impulse);
if (jumpDirection.x>=0.9)
{
transform.DOLocalRotate(new Vector3(0, 0, -360), 0.6f, RotateMode.LocalAxisAdd);
}
else
{
transform.DOLocalRotate(new Vector3(-360, 0, 0), 0.6f, RotateMode.LocalAxisAdd);
}
}
/// <summary>
/// 碰撞的事件
/// </summary>
/// <param name="collision"></param>
private void OnCollisionEnter(Collision collision)
{
Rig.Sleep();
if (GameOver.activeInHierarchy)
{
return;
}
if (!collision.transform.CompareTag("Stage"))
{
PlayerDie();
return;
}
Transform rolesPos = collision.transform.Find("RolesPos");
rolesPos.position = transform.position;
if(rolesPos.localPosition.x<0.5f&& rolesPos.localPosition.x > -0.5f)
{
if(rolesPos.localPosition.z < 0.5f && rolesPos.localPosition.z > -0.5f)
{
//当我们的身体有一半在舞台的外部,就算游戏结束。
//说明没有掉下舞台
if (CurrentStage != collision.gameObject)
{//如果碰撞到的不是当前的舞台 //把碰撞到的舞台设置为当前的
CurrentStage = collision.gameObject;
//添加分数
AddScore();
//生成出新的舞台
SpawnStage();
//移动相机
MoveCamera();
//重新允许用户输入
EnableInput = true;
//播放音效
PlayAudio(SuccessAudio);
}
else
{
EnableInput = true;
}
}
else
{
//游戏结束
PlayerDie();
}
}
else
{
//游戏结束
PlayerDie();
}
}
/// <summary>
/// 角色死亡处理
/// </summary>
private void PlayerDie()
{
EnableInput = false;
#if UNITY_ANDROID//如果是安卓平台,
OpendAdMobTips(); //提示是否观看广告
#elif UNITY_IPHONE//如果是IPHONE
OpendAdMobTips(); //提示是否观看广告
#else//如果不是安卓也不是IPHONE平台的
OnGameOver();
#endif
//播放失败的音效
PlayAudio(FallAudio);
}
/// <summary>
/// 提示是否观看广告
/// </summary>
private void OpendAdMobTips()
{
AdMobTips.gameObject.SetActive(true);
}
//添加分数
private void AddScore()
{
//首先获取人物的位置,也就是命中的位置
Vector3 hitPos = transform.position;
hitPos.y = CurrentStage.transform.position.y;
//计算与目标距离
float targetDistance = Vector3.Distance(hitPos, CurrentStage.transform.position);
if (targetDistance < 0.1f)
{
//如果离中心非常近,要加的分数翻倍
LastReward *= 2;
}
else
{
LastReward = 1;
}
Score += LastReward;
ScoreText.text = "得分:" + Score;
}
/// <summary>
/// 移动摄像机跟随人物
/// </summary>
private void MoveCamera()
{
Camera.main.transform.DOMove(transform.position + CameraRelativePosition, 1);
}
/// <summary>
/// 结束游戏 首先是PC端,人物死亡后调用
/// 然后移动端,看完广告之后调用
/// </summary>
public void OnGameOver()
{
AdMobTips.gameObject.SetActive(false);
//显示最终得分
FinalScore.text = "最终得分:" + Score;
//开启重新开始的按钮
RestartButton.gameObject.SetActive(true);
FinalScore.gameObject.SetActive(true);
GameOver.SetActive(true);
}
/// <summary>
/// 播放音效
/// </summary>
/// <param name="audioClip"></param>
private void PlayAudio(AudioClip audioClip)
{
audioSource.clip = audioClip;
audioSource.Play();
}
}