Unity 游戏实例开发集合 之 JumpJump (简单跳一跳) 休闲小游戏快速实现
目录
Unity 游戏实例开发集合 之 JumpJump (简单跳一跳) 休闲小游戏快速实现
一、简单介绍
二、JumpJump (简单跳一跳)游戏内容与操作
三、游戏代码框架
四、知识点
五、游戏效果预览
六、实现步骤
七、工程源码地址
八、延伸扩展
一、简单介绍
Unity 游戏实例开发集合,使用简单易懂的方式,讲解常见游戏的开发实现过程,方便后期类似游戏开发的借鉴和复用。
本节介绍,JumpJump (简单跳一跳) 休闲小游戏快速实现的方法,希望能帮到你,若有不对,请留言。
二、JumpJump (简单跳一跳)游戏内容与操作
1、游戏开始,会自动生成平台
2、鼠标按下左键角色会自动朝向平台方向,长按住右键进行蓄力
3、松开鼠标左键,角色就会根据蓄力多少,进行抛物线跳跃
4、落到平台上,则对应加分,落下平台,游戏就结束
5、游戏结束后,这里会自动重新开始游戏
三、游戏代码框架
四、知识点
1、MonoBehaviour 生命周期函数:Awake,Start,Update,Destroy,OnGUI
2、Input 按键的监控鼠标按键的状态
3、GameObject.Instantiate 物体的生成,GameObject.Destroy 物体的销毁
4、Camera 的跟随管理
5、Rigidbody2D 重力效果,添加 Collider ,进行 碰撞检测
6、GUIStyle GUI样式,GUI.Label 添加文字的功能使用
7、Vector3.Lerp 位移向量插值使用,Vector3.Distance 位置距离函数的使用
8、一些数据,路径等的统一常量管理
9、Transform.Rotate 旋转使用
10、IEnumerator 协程 , StartCoroutine 开始协程 和 StopAllCoroutines 停止所有协程的使用
11、物体简单抛物线运动的使用
12、Action<int> OnChangeValue 属性变化中委托的使用
13、Resources.Load<GameObject>() 代码加载预制体的使用
14、Fog 雾效的简单使用,和渐变背景的简单实现
15、 SceneManager.LoadScene 加载,和 SceneManager.GetActiveScene() 当前场景的获取
16、Lighting Settings 场景光线烘培的简单使用
17、等等
五、游戏效果预览
六、实现步骤
这是一个 3D 游戏,主要用到 SpriteRenderer 、2D Collider,2D Rigidbody,以及 TextMesh 等资源组件,所有资源都是Unity自带,没有导入其他外部贴图模型资源。
1、首先,做一个简单的渐变的背景,添加一个 Plane ,设置如下,并添加一个材质
2、打开设置,开启雾效Fog,这里是渐变背景断网关键
3、在Lighting 的 Other Settings 中勾选 Fog,设置颜色,Mode 为 Liner ,处设置 Start 10,End 13(后面在调整细节)
3、添加一个 Cube,设置 scale 为 (1,8,1)?
4、设置 Sky_Plane 的 Transform 和 Main Camera(旋转摄像机是为了渐变从下到上(上面显示 Sky_Plane的颜色)) 的 Transform 如图,效果大概如下(设置竖屏(800x1280))
5、再次调整 Sky_Plane 的 Rotation X,和 Lighting 的 Other Settings Fog 的 Start 和 End,最后效果如下
6、这里我们不需要 Sky_Plane 接收投影和 Collider ,这里去掉 Mesh Collider 组件
7、新建一个空物体GameObject,Reset 其 Transform ,把 MainCamera 和 Sky_Plane 作为其子物体,并改为为 CameraGroup,便于后期跟随主角 Player 移动
8、根据 Cube ,创建平台几种颜色的 Platform,并取消他们 投射阴影 Cast Shadows 为 off (注意:他们的子物体都取消 Collider 组件)
9、创建一个空物体 GameObject ,改名为 Player,添加一个 Rigbody ,FreezeRotation 的 X Y Z
10、Player 添加一个 Capsule,改名为 Player_Capsule,Scale根据 Platform 的 比例进行调整,所以这里调小为 (0.5,0.5,0.5),这里调整 Position 的 Y 为 0.5 是为了使得Player_Capsule的底部至于 Player 局部坐标的原点,便于 Y 上 Scale 的变化(变化的基点从底部缩放)如图,最后 Sphere 作为前方向的辨识使用而已(去掉Collider 组件)
11、在 Player 添加 TrailRenderer ,添加运动的拖尾效果(效果根据实际效果调整),这里设置宽度为 0.5 的渐变减小,时间为 0.5,颜色如图,添加 TrailRenderer 材质,,材质 shader 为 Particles /Additive,效果如下
12、把 Player 作为预制体,放在 resources/Prefabs 文件夹下
13、添加一个 UI Text ,用来显示分数
14、添加空物体GameObject,改名为 World,把CameraGroup,置于其下,并合理设置 GameObject 的 PlayerSpawnPos ,FirstPlatFormCubeSpawnPos位置,作为 Player 和Platform 的初始化生成位置
15、在工程中添加 Platform 脚本,主要功能是继承 MonoBehaviour,在 Update 中控制 Platform 的生成动画,和 Player 跳入和触发 OnCollisionEnter 事件,进行 生成下一个 Platform,以及增加分数
16、Platform 中的主要函数,PlatformSpawnAniamtion 和? FirstOnCollisionEnter
/// <summary>
/// 平台的上升动画
/// </summary>
void PlatformSpawnAniamtion() {
if (m_IsSpawnAnimation == true)
{
m_MoveDeltra += 1f / GameConfig.PLATFRM_SPAWN_ANIMATION_MOVE_SPEED * Time.deltaTime;
transform.position = Vector3.Lerp(transform.position, m_TargetPos, m_MoveDeltra);
if (Vector3.Distance(transform.position, m_TargetPos) <= 0.01f)
{
transform.position = m_TargetPos;
m_IsSpawnAnimation = false;
}
}
}
/// <summary>
/// 第一次碰撞进入平台,触发对应事件
/// 这里事件主要是 生成下一个 Platform 和 增加分数
/// </summary>
private void FirstOnCollisionEnter()
{
if (m_IsOnCollisionEntered == false)
{
m_IsOnCollisionEntered = true;
m_OnCollisionEnterAction?.Invoke(this.transform.position);
}
}
17、在工程中添加 PlatformManager 脚本,主要功能是,加载所有类型的 Platform 预制体,初始化第一个随机 Platform 平台生成,并且设置平台生成下一个 Platform 的事件,并控制游戏中保留的 Platform? 的总数,多则删除最先的 Platform
18、PlatformManager 脚本的主要函数:LoadPlatformPrefabs() 加载 Platform 预制体,SetOnPlatformCubeEnterAction() 设置平台回调事件,SpawnNext(Vector3 curPos, int score, Transform parent = null)随机生成下一个 Platform 方向,并 生成下一个 Platform,?Spawn(Vector3 pos, int score, Transform parent = null, bool isMoveAnimation = false) 随机生成一个 Platform,JudgePlatformQueueCount() 控制场景中 Platform 的数量,过多则删除最初的 Platform
/// <summary>
/// 加载 Platform 预制体
/// </summary>
void LoadPlatformPrefabs() {
foreach (string resPath in ResPathDefine.PLATFORM_RES_PATH_LIST)
{
GameObject prefab = Resources.Load<GameObject>(resPath);
if (prefab != null)
{
m_PlatformPrefabList.Add(prefab);
}
else {
Debug.LogError(GetType()+ "/LoadPlatformPrefabs()/ prefab is null, resPath = " + resPath );
}
}
}
/// <summary>
/// 设置平台回调事件(这个主要是分数事件)
/// </summary>
/// <param name="onPlatformEnterAction"></param>
void SetOnPlatformCubeEnterAction(Action<int> onPlatformEnterAction)
{
m_OnPlatformEnterAction = onPlatformEnterAction;
}
/// <summary>
/// 随机生成下一个 Platform 方向,并 生成下一个 Platform
/// </summary>
/// <param name="curPos">位置</param>
/// <param name="score">增加的分数</param>
/// <param name="parent">附载的父物体</param>
/// <returns></returns>
GameObject SpawnNext(Vector3 curPos, int score, Transform parent = null)
{
int rand = UnityEngine.Random.Range(0, (int)Dir.ENUM_COUNT);
m_CurDir = (Dir)rand;
switch (m_CurDir)
{
case Dir.Right:
curPos.x += GameConfig.PLATFRM_CUBE_DISTANCE;
break;
case Dir.Forword:
curPos.z += GameConfig.PLATFRM_CUBE_DISTANCE;
break;
case Dir.ENUM_COUNT:
break;
default:
break;
}
return Spawn(curPos, score,parent, true);
}
/// <summary>
/// 随机生成一个 Platform
/// </summary>
/// <param name="curPos">位置</param>
/// <param name="score">增加的分数</param>
/// <param name="parent">附载的父物体</param>
/// <param name="isMoveAnimation">是否进行上升动画</param>
/// <returns></returns>
GameObject Spawn(Vector3 pos, int score, Transform parent = null, bool isMoveAnimation = false)
{
int randValue = UnityEngine.Random.Range(0, m_PlatformPrefabList.Count);
GameObject go = GameObject.Instantiate(m_PlatformPrefabList[randValue], parent);
m_PlatformsQueue.Enqueue(go);
Platform platform = go.GetComponent<Platform>();
if (platform==null)
{
platform = go.AddComponent<Platform>();
}
platform.Init(
(curPos) => {
m_CurPlatformCube = m_NextPlatformCube;
m_NextPlatformCube = SpawnNext(curPos, score, parent);
if (m_OnPlatformEnterAction!=null)
{
m_OnPlatformEnterAction.Invoke(score);
}
},
pos,
isMoveAnimation);
return go;
}
/// <summary>
/// 控制场景中 Platform 的数量
/// 过多则删除最初的 Platform
/// </summary>
void JudgePlatformQueueCount() {
if (m_PlatformsQueue.Count> GameConfig.PLATFORM_EXIST_COUNT)
{
GameObject.Destroy(m_PlatformsQueue.Dequeue());
}
}
19、在工程中添加 Player 脚本,主要功能是,监听 Jump 相关事件(鼠标左键按下,抬起等),根据按下蓄力时长,生成运动轨迹,并监听 Player 是否坠落 Platform ,继承 Monobehaviour ,监听碰撞事件等相关功能
20、Player 脚本主要函数:UpdateJumpOperation() ump 跳一跳相关鼠标监听事件,OnCollisionEnter(Collision collision) 碰撞事件,在平台才能 Jump
/// <summary>
/// Jump 跳一跳相关鼠标监听事件
/// </summary>
public void UpdateJumpOperation()
{
if (m_IsFallen == true)
{
return;
}
JudgeFallen();
if (m_IsCanJump == false)
{
return;
}
if (Input.GetMouseButtonDown(0))
{
UpdatePlayerRotate();
m_PressTime = 0;
}
if (Input.GetMouseButton(0))
{
m_PressTime += Time.deltaTime;
if (m_PressTime >= GameConfig.MOUSE_PRESSING_TIME_LENGTH)
{
m_PressTime = GameConfig.MOUSE_PRESSING_TIME_LENGTH;
}
SmallScaleYAnimation();
}
if (Input.GetMouseButtonUp(0))
{
StartCoroutine(Jump());
BackOriScale();
m_IsCanJump = false;
}
}
#region Unity Functions
/// <summary>
/// 碰撞事件,在平台才能 Jump
/// </summary>
/// <param name="collision"></param>
private void OnCollisionEnter(Collision collision)
{
m_IsCanJump = true;
// 第一碰撞平台的基础数据设置
if (m_IsInitCollisionEnter == false)
{
m_IsInitCollisionEnter = true;
m_ModelOriPos = this.transform.position;
}
}
#endregion
21、Player 脚本主要函数:IEnumerator Jump() 协程,进行跳跃实现, JumpMoving(float jumpLength) 根据蓄力结果,生成跳跃轨迹,YParabola(float x, float k, float top)计算抛物线的 Y 值,这里需要简单的抛物线知识,进行一些转化,有兴趣的同学也可以自行实现
/// <summary>
/// 协程,进行跳跃实现
/// </summary>
/// <returns></returns>
IEnumerator Jump()
{
float jumpLength = m_PressTime * GameConfig.PLAYER_MODEL_JUMP_LENGTH_SMOOTH_VALUE;
oriPos = this.transform.localPosition;
Vector3 nextPos = oriPos + transform.forward * jumpLength;
movePos = nextPos - oriPos;
m_JumpTime = m_JumpTime_Length;
while (true)
{
if (m_JumpTime < 0)
{
break;
}
JumpMoving(jumpLength);
yield return new WaitForEndOfFrame();
}
}
/// <summary>
/// 根据蓄力结果,生成跳跃轨迹
/// </summary>
/// <param name="jumpLength"></param>
void JumpMoving(float jumpLength)
{
m_JumpTime -= Time.deltaTime;
float deltra = (m_JumpTime_Length - m_JumpTime) / m_JumpTime_Length;
var x = oriPos.x + movePos.x * deltra;
var y = oriPos.y + YParabola(jumpLength * deltra, jumpLength / 2, GameConfig.PLAYER_MODEL_JUMP_TOP_DISTANCE);
var z = oriPos.z + movePos.z * deltra;
this.transform.localPosition = new Vector3(x, y, z);
}
/// <summary>
/// 计算抛物线的 Y 值
/// </summary>
/// <param name="x"></param>
/// <param name="k"></param>
/// <param name="top">抛物线最高的点高度</param>
/// <returns></returns>
float YParabola(float x, float k, float top)
{
if (k == 0)
{
k = 1;
}
return top - (top * (x - k) * (x - k) / (k * k));
}
22、Player 脚本主要函数:UpdatePlayerRotate()根据 Platform 位置,Player 进行转向,SmallScaleYAnimation()蓄力 Player 的动画,BackOriScale()回到蓄力动画前,JudgeFallen()判断 Player 是否坠落 Platform
/// <summary>
/// 根据 Platform 位置,Player 进行转向
/// </summary>
void UpdatePlayerRotate()
{
float angle = 0;
switch (m_PlatformManager.CurDir)
{
case Dir.Right:
angle = Vector3.Angle(transform.forward, Vector3.right);
this.transform.Rotate(Vector3.up, angle);
break;
case Dir.Forword:
angle = Vector3.Angle(transform.forward, Vector3.forward);
this.transform.Rotate(Vector3.up, -angle);
break;
default:
break;
}
}
/// <summary>
/// 蓄力 Player 的动画
/// </summary>
void SmallScaleYAnimation()
{
if (this.transform.localScale.y <= m_ModelMinScaleY)
{
return;
}
this.transform.localScale = Vector3.Lerp(this.transform.localScale,
m_ModelMinScale,
Time.deltaTime * GameConfig.PLAYER_MODEL_SCALE_Y_SAMLL_SPEED);
}
/// <summary>
/// 回到蓄力动画前
/// </summary>
void BackOriScale()
{
this.transform.localScale = m_ModelOriScale;
}
/// <summary>
/// 判断 Player 是否坠落 Platform
/// </summary>
void JudgeFallen()
{
if (this.transform.position.y < m_ModelOriPos.y - GameConfig.PLAYER_MODEL_FALL_DISTANCE_FROM_PLATFORM)
{
m_IsFallen = true;
}
}
23、在工程中添加 PlayerManager 脚本,主要功能是获取 Player 预制体,在场景中生成 Player
24、 PlayerManager 脚本主要函数是:IsFallen() Player 是否坠落,SpawnPlayer(Vector3 pos,Transform parent) 指定位置生成 Player
/// <summary>
/// Player 是否坠落
/// </summary>
/// <returns></returns>
public bool IsFallen() {
return m_Player.IsFallen;
}
/// <summary>
/// 指定位置生成 Player
/// </summary>
/// <param name="pos">位置</param>
/// <param name="parent">父物体</param>
/// <returns></returns>
Player SpawnPlayer(Vector3 pos,Transform parent) {
Player player = null;
if (m_PlayerPrefab==null)
{
GameObject prefab = Resources.Load<GameObject>(ResPathDefine.PLAYER_RES_PATH);
if (prefab != null)
{
m_PlayerPrefab = (prefab);
}
else
{
Debug.LogError(GetType() + "/LoadPlatformPrefabs()/ prefab is null, resPath = " + ResPathDefine.PLAYER_RES_PATH);
}
}
player = GameObject.Instantiate<GameObject>(m_PlayerPrefab, pos, Quaternion.identity).AddComponent<Player>();
player.transform.SetParent(parent);
return player;
}
25、在工程中添加 CameraManager 脚本,主要功能是,使得 Camera 跟随 Player 跳到的 Platform ,进行移动,追随 Player,SetOffSet()?Camera 和 Platform 的位置差值
26、CameraManager 脚本主要函数:SetCameraTransPos() Camera 动画移动到指定 Platform
/// <summary>
/// Camera 动画移动到指定 Platform
/// </summary>
void SetCameraTransPos()
{
if (m_PlatformManager.CurPlatformCube != null)
{
m_CameraTrans.position = Vector3.Lerp(m_CameraTrans.position,
m_PlatformManager.CurPlatformCube.transform.position - m_OffsetPos,
Time.deltaTime * GameConfig.CAMERA_FOLLOW_PLAYER_SPEED);
}
}
/// <summary>
/// Camera 和 Platform 的位置差值
/// </summary>
void SetOffSet()
{
m_OffsetPos = m_PlatformManager.CurPlatformCube.transform.position - m_CameraTrans.position;
}
27、在工程中添加 ScoreManager 脚本,主要功能是,进行分数的统计,和分数变化事件的触发
28、在工程中添加 GameManager 脚本,主要功能是,获取场景中的一些游戏物体,管理各个 Manager 的初始化,Update ,Destroy 等,判断游戏是否结束,结束则重新开始游戏
29、GameManager 脚本主要函数是:Awake(),Start(),Update(),OnDestroy() 进行管理对应 Manager 的相关初始化,Update,Destroy 函数,OnGUI() 设置简单的操作说明
public void Awake()
{
PlatformManager = new PlatformManager();
PlayerManager = new PlayerManager();
CameraManager = new CameraManager();
ScoreManager = new ScoreManager();
}
public void Start()
{
FindGameObjectInScene();
PlatformManager.Init(OnPlatformEnterAction, m_FirstPlatFormCubeSpawnPosTrans.position, GameConfig.PLATFORM_ADD_SCORE, m_FirstPlatFormCubeSpawnPosTrans);
PlayerManager.Init(m_PlayerSpawnPosTrans, PlatformManager);
CameraManager.Init(m_CameraGroupTrans,PlatformManager);
ScoreManager.Score = 0;
ScoreManager.OnValueChanged += (socre) => { m_ScoreText.text = ScoreManager.Score.ToString(); };
}
public void Update()
{
JudgeGameOver();
PlatformManager.Update();
PlayerManager.Update();
CameraManager.Update();
}
public void OnDestroy()
{
PlatformManager.Destroy();
PlayerManager.Destroy();
CameraManager.Destroy();
m_WorldTrans = null;
m_UITrans = null;
m_FirstPlatFormCubeSpawnPosTrans = null;
m_PlayerSpawnPosTrans = null;
m_CameraGroupTrans = null;
m_ScoreText = null;
m_IsGameOver = false;
PlatformManager = null;
PlayerManager = null;
CameraManager = null;
ScoreManager = null;
}
public void OnGUI()
{
// 游戏操作说明
GUIStyle fontStyle = new GUIStyle();
fontStyle.normal.background = null; //设置背景填充
fontStyle.normal.textColor = new Color(1, 0, 0); //设置字体颜色
fontStyle.fontSize = 32; //字体大小
GUI.Label(new Rect(10, 10, 200, 200),
"操作说明:\n1、按下鼠标左键蓄力;\n2、松开鼠标左键起跳;\n3、坠落,重新开始;",
fontStyle);
}
30、GameManager 脚本主要函数是:FindGameObjectInScene() 获取场景中的游戏物体,JudgeGameOver()判断游戏是否结束,OnPlatformEnterAction(int score)分数增加委托
/// <summary>
/// 获取场景中的游戏物体
/// </summary>
private void FindGameObjectInScene()
{
m_WorldTrans = GameObject.Find(GameObjectPathInSceneDefine.WORLD_PATH).transform;
m_UITrans = GameObject.Find(GameObjectPathInSceneDefine.UI_PATH).transform;
m_FirstPlatFormCubeSpawnPosTrans = m_WorldTrans.Find(GameObjectPathInSceneDefine.FIRST_PLATFORM_CUBE_SPAWN_POS_PATH);
m_PlayerSpawnPosTrans = m_WorldTrans.Find(GameObjectPathInSceneDefine.PLAYER_SPAWN_POS_PATH);
m_CameraGroupTrans = m_WorldTrans.Find(GameObjectPathInSceneDefine.CAMERA_GROUP_PATH);
m_ScoreText = m_UITrans.Find(GameObjectPathInSceneDefine.CANVAS_SCORE_TEXT_PATH).GetComponent<Text>() ;
}
/// <summary>
/// 判断游戏是否结束
/// </summary>
void JudgeGameOver()
{
if (PlayerManager.IsFallen() == true)
{
if (m_IsGameOver == false)
{
m_IsGameOver = true;
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
}
}
}
/// <summary>
/// 分数增加委托
/// </summary>
/// <param name="score"></param>
void OnPlatformEnterAction(int score) {
ScoreManager.Score += score;
}
31、在工程中添加 GameStart 脚本,主要功能是,整个游戏的入口,管理对应 GameManager 的 Awake(),Start(),Update(),OnDestroy() ,OnGUI() 对应函数功能
32、在工程中添加 Enum、GameConfig、GameObjectPathInSceneDefine、ResPathDefine脚本,Enum 管理所有枚举,GameConfig 一些控制游戏效果的游戏配置常量定义,GameObjectPathInSceneDefine 管理场景中游戏物体的路径常量定义,ResPathDefine 是 Resources 管理预制体的路径常量定义
33、在场景中添加 GameObject 空物体,改名为 GameStart,并且挂载 GameStart 脚本
34、运行场景,场景会自动,生成 Platform 、 Player 和分数显示等,通过鼠标即可对应操作了
35、因为这里进行了自动灯光处理,所以这里,进行场景的光线效果烘焙
36、烘培之后,效果如下,明显光亮艳丽多了
七、工程源码地址
github 地址:GitHub - XANkui/UnityMiniGameParadise: Unity 游戏开发集合代码集
的 MGP_003JumpJump 工程
八、延伸扩展
游戏的好不好玩,趣味性,视觉化等诸多因素影响,下面简单介绍几个方面拓展游戏的方向,仅做参考
1、可以根据自己需要修改游戏资源,换肤什么的等
2、可以根据需要添加加分特效,音效,背景更多的细节变化等等
3、添加 UI 面板等,美化游戏
4、可不设置不同的平台除了基础分数的特殊事件,比例在平台停留超过多久,触发特殊分数或者事件等等
5、平台现在每次的间距固定,可以随机间距间于范围内即可;
6、添加最高分数保留,和游戏排行榜等;
7、优化 Platform 管理,使用对象池管理优化性能等等;
|