Unity 游戏实例开发集合 之 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 OnChangeValue 属性变化中委托的使用
13、Resources.Load() 代码加载预制体的使用
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.53D角色,颜色如图,添加 TrailRenderer 材质,,材质 shader 为 Particles /Additive,效果如下
12、把 Player 作为预制体,放在 resources/Prefabs 文件夹下
13、添加一个 UI Text ,用来显示分数
14、添加空物体GameObject,改名为 World2d素材,把CameraGroup,置于其下,并合理设置 GameObject 的 PlayerSpawnPos ,FirstPlatFormCubeSpawnPos位置,作为 Player 和Platform 的初始化生成位置
15、在工程中添加 Platform 脚本,主要功能是继承 MonoBehaviour,在 Update 中控制 Platform 的生成动画,和 Player 跳入和触发 OnCollisionEnter 事件,进行 生成下一个 Platform,以及增加分数
16、Platform 中的主要函数,PlatformSpawnAniamtion 和 FirstOnCollisionEnter
///
/// 平台的上升动画
///
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;
}
}
}
///
/// 第一次碰撞进入平台,触发对应事件
/// 这里事件主要是 生成下一个 Platform 和 增加分数
///
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
///
/// 加载 Platform 预制体
///
void LoadPlatformPrefabs() {
foreach (string resPath in ResPathDefine.PLATFORM_RES_PATH_LIST)
{
GameObject prefab = Resources.Load(resPath);
if (prefab != null)
{
m_PlatformPrefabList.Add(prefab);
}
else {
Debug.LogError(GetType()+ "/LoadPlatformPrefabs()/ prefab is null, resPath = " + resPath );
}
}
}
///
/// 设置平台回调事件(这个主要是分数事件)
///
///
void SetOnPlatformCubeEnterAction(Action onPlatformEnterAction)
{
m_OnPlatformEnterAction = onPlatformEnterAction;
}
///
/// 随机生成下一个 Platform 方向,并 生成下一个 Platform
///
/// 位置
/// 增加的分数
/// 附载的父物体
///
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);
}
///
/// 随机生成一个 Platform
///
/// 位置
/// 增加的分数
/// 附载的父物体
/// 是否进行上升动画
///
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();
if (platform==null)
{
platform = go.AddComponent();
}
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;
}
///
/// 控制场景中 Platform 的数量
/// 过多则删除最初的 Platform
///
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
///
/// Jump 跳一跳相关鼠标监听事件
///
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
///
/// 碰撞事件,在平台才能 Jump
///
///
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 值,这里需要简单的抛物线知识,进行一些转化,有兴趣的同学也可以自行实现
///
/// 协程,进行跳跃实现
///
///
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();
}
}
///
/// 根据蓄力结果,生成跳跃轨迹
///
///
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);
}
///
/// 计算抛物线的 Y 值
///
///
///
/// 抛物线最高的点高度
///
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
///
/// 根据 Platform 位置,Player 进行转向
///
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;
}
}
///
/// 蓄力 Player 的动画
///
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);
}
///
/// 回到蓄力动画前
///
void BackOriScale()
{
this.transform.localScale = m_ModelOriScale;
}
///
/// 判断 Player 是否坠落 Platform
///
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
///
/// Player 是否坠落
///
///
public bool IsFallen() {
return m_Player.IsFallen;
}
///
/// 指定位置生成 Player
///
/// 位置
/// 父物体
///
Player SpawnPlayer(Vector3 pos,Transform parent) {
Player player = null;
if (m_PlayerPrefab==null)
{
GameObject prefab = Resources.Load(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(m_PlayerPrefab, pos, Quaternion.identity).AddComponent();
player.transform.SetParent(parent);
return player;
}
25、在工程中添加 CameraManager 脚本,主要功能是,使得 Camera 跟随 Player 跳到的 Platform ,进行移动,追随 Player,SetOffSet()Camera 和 Platform 的位置差值
26、CameraManager 脚本主要函数:SetCameraTransPos() Camera 动画移动到指定 Platform
///
/// Camera 动画移动到指定 Platform
///
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);
}
}
///
/// Camera 和 Platform 的位置差值
///
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)分数增加委托
///
/// 获取场景中的游戏物体
///
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() ;
}
///
/// 判断游戏是否结束
///
void JudgeGameOver()
{
if (PlayerManager.IsFallen() == true)
{
if (m_IsGameOver == false)
{
m_IsGameOver = true;
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
}
}
}
///
/// 分数增加委托
///
///
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 管理,使用对象池管理优化性能等等;
文章来源:https://blog.csdn.net/u014361280/article/details/122478869