Unity 游戏实例开发集合 之 JumpJump (简单跳一跳) 休闲小游戏快速实现

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 管理,使用对象池管理优化性能等等;

猜你喜欢

转载自blog.csdn.net/u014361280/article/details/122478869