Unity3D开发小游戏Unity3D开发《3D迷宫》小游戏
Posted 恬静的小魔龙
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity3D开发小游戏Unity3D开发《3D迷宫》小游戏相关的知识,希望对你有一定的参考价值。
推荐阅读
大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有用记得一键三连哦。
一、前言
闲来无事,从零开始整个《3D迷宫》小游戏。
本篇文章会详细介绍构思、实现思路,希望可以帮助到有缘人。
二、构思
首先,要实现一个小游戏,心里肯定要有一个大概的想法,然后就是将想法完善起来。
我的想法就是一个用立体的墙搭建的迷宫,然后控制人物在迷宫中移动,最后找到出口,就这么简单。
当然,这是一个雏形,比如可以加点音效、背景、关卡、解密等。
那么整理一下实现思路就是:
- 构建3D迷宫
- 实现人物移动
- 实现出入口逻辑
OK,下面就正式开发。
三、正式开发
3-1、搭建场景
首先,新建个项目,我用了Unity 2019.4.7f1
版本,项目名称跟位置按照自己的喜好设置即可:
接下来构建迷宫,先新建一个Plane,让它最够大,扩大10倍:
新建Cube,调整大小缩放,让它看起来像是一堵墙,然后构建迷宫:
3-2、设置出入口
放两个Cube,设置缩放,将出口名字改成Exit,这样就行了,到时候通过碰撞检测检测小球是否到达出口即可。
3-3、添加角色
在Hierarchy视图,右击选择3D Objcet→Capsule,新建一个球体,添加Rigibody组件:
设置Drag抓地力为1。
就这样设置就行了,在实际运行中如果参数不合适还可以再调整。
将小球移动到入口的位置。
3-4、实现角色移动
这里直接使用官方的第一人称移动代码RigidbodyFirstPersonController .cs:
public class RigidbodyFirstPersonController : MonoBehaviour
[Serializable]
public class MovementSettings
public float ForwardSpeed = 8.0f; // Speed when walking forward
public float BackwardSpeed = 4.0f; // Speed when walking backwards
public float StrafeSpeed = 4.0f; // Speed when walking sideways
public float RunMultiplier = 2.0f; // Speed when sprinting
public KeyCode RunKey = KeyCode.LeftShift;
public float JumpForce = 30f;
public AnimationCurve SlopeCurveModifier = new AnimationCurve(new Keyframe(-90.0f, 1.0f), new Keyframe(0.0f, 1.0f), new Keyframe(90.0f, 0.0f));
[HideInInspector] public float CurrentTargetSpeed = 8f;
#if !MOBILE_INPUT
private bool m_Running;
#endif
public void UpdateDesiredTargetSpeed(Vector2 input)
if (input == Vector2.zero) return;
if (input.x > 0 || input.x < 0)
//strafe
CurrentTargetSpeed = StrafeSpeed;
if (input.y < 0)
//backwards
CurrentTargetSpeed = BackwardSpeed;
if (input.y > 0)
//forwards
//handled last as if strafing and moving forward at the same time forwards speed should take precedence
CurrentTargetSpeed = ForwardSpeed;
#if !MOBILE_INPUT
if (Input.GetKey(RunKey))
CurrentTargetSpeed *= RunMultiplier;
m_Running = true;
else
m_Running = false;
#endif
#if !MOBILE_INPUT
public bool Running
get return m_Running;
#endif
[Serializable]
public class AdvancedSettings
public float groundCheckDistance = 0.01f; // distance for checking if the controller is grounded ( 0.01f seems to work best for this )
public float stickToGroundHelperDistance = 0.5f; // stops the character
public float slowDownRate = 20f; // rate at which the controller comes to a stop when there is no input
public bool airControl; // can the user control the direction that is being moved in the air
[Tooltip("set it to 0.1 or more if you get stuck in wall")]
public float shellOffset; //reduce the radius by that ratio to avoid getting stuck in wall (a value of 0.1f is nice)
public Camera cam;
public MovementSettings movementSettings = new MovementSettings();
public MouseLook mouseLook = new MouseLook();
public AdvancedSettings advancedSettings = new AdvancedSettings();
private Rigidbody m_RigidBody;
private CapsuleCollider m_Capsule;
private float m_YRotation;
private Vector3 m_GroundContactNormal;
private bool m_Jump, m_PreviouslyGrounded, m_Jumping, m_IsGrounded;
public Vector3 Velocity
get return m_RigidBody.velocity;
public bool Grounded
get return m_IsGrounded;
public bool Jumping
get return m_Jumping;
public bool Running
get
#if !MOBILE_INPUT
return movementSettings.Running;
#else
return false;
#endif
private void Start()
m_RigidBody = GetComponent<Rigidbody>();
m_Capsule = GetComponent<CapsuleCollider>();
mouseLook.Init (transform, cam.transform);
private void Update()
RotateView();
if (CrossPlatformInputManager.GetButtonDown("Jump") && !m_Jump)
m_Jump = true;
private void FixedUpdate()
GroundCheck();
Vector2 input = GetInput();
if ((Mathf.Abs(input.x) > float.Epsilon || Mathf.Abs(input.y) > float.Epsilon) && (advancedSettings.airControl || m_IsGrounded))
// always move along the camera forward as it is the direction that it being aimed at
Vector3 desiredMove = cam.transform.forward*input.y + cam.transform.right*input.x;
desiredMove = Vector3.ProjectOnPlane(desiredMove, m_GroundContactNormal).normalized;
desiredMove.x = desiredMove.x*movementSettings.CurrentTargetSpeed;
desiredMove.z = desiredMove.z*movementSettings.CurrentTargetSpeed;
desiredMove.y = desiredMove.y*movementSettings.CurrentTargetSpeed;
if (m_RigidBody.velocity.sqrMagnitude <
(movementSettings.CurrentTargetSpeed*movementSettings.CurrentTargetSpeed))
m_RigidBody.AddForce(desiredMove*SlopeMultiplier(), ForceMode.Impulse);
if (m_IsGrounded)
m_RigidBody.drag = 5f;
if (m_Jump)
m_RigidBody.drag = 0f;
m_RigidBody.velocity = new Vector3(m_RigidBody.velocity.x, 0f, m_RigidBody.velocity.z);
m_RigidBody.AddForce(new Vector3(0f, movementSettings.JumpForce, 0f), ForceMode.Impulse);
m_Jumping = true;
if (!m_Jumping && Mathf.Abs(input.x) < float.Epsilon && Mathf.Abs(input.y) < float.Epsilon && m_RigidBody.velocity.magnitude < 1f)
m_RigidBody.Sleep();
else
m_RigidBody.drag = 0f;
if (m_PreviouslyGrounded && !m_Jumping)
StickToGroundHelper();
m_Jump = false;
private float SlopeMultiplier()
float angle = Vector3.Angle(m_GroundContactNormal, Vector3.up);
return movementSettings.SlopeCurveModifier.Evaluate(angle);
private void StickToGroundHelper()
RaycastHit hitInfo;
if (Physics.SphereCast(transform.position, m_Capsule.radius * (1.0f - advancedSettings.shellOffset), Vector3.down, out hitInfo,
((m_Capsule.height/2f) - m_Capsule.radius) +
advancedSettings.stickToGroundHelperDistance, Physics.AllLayers, QueryTriggerInteraction.Ignore))
if (Mathf.Abs(Vector3.Angle(hitInfo.normal, Vector3.up)) < 85f)
m_RigidBody.velocity = Vector3.ProjectOnPlane(m_RigidBody.velocity, hitInfo.normal);
private Vector2 GetInput()
Vector2 input = new Vector2
x = CrossPlatformInputManager.GetAxis("Horizontal"),
y = CrossPlatformInputManager.GetAxis("Vertical")
;
movementSettings.UpdateDesiredTargetSpeed(input);
return input;
private void RotateView()
//avoids the mouse looking if the game is effectively paused
if (Mathf.Abs(Time.timeScale) < float.Epsilon) return;
// get the rotation before it's changed
float oldYRotation = transform.eulerAngles.y;
mouseLook.LookRotation (transform, cam.transform);
if (m_IsGrounded || advancedSettings.airControl)
// Rotate the rigidbody velocity to match the new direction that the character is looking
Quaternion velRotation = Quaternion.AngleAxis(transform.eulerAngles.y - oldYRotation, Vector3.up);
m_RigidBody.velocity = velRotation*m_RigidBody.velocity;
/// sphere cast down just beyond the bottom of the capsule to see if the capsule is colliding round the bottom
private void GroundCheck()
m_PreviouslyGrounded = m_IsGrounded;
RaycastHit hitInfo;
if (Physics.SphereCast(transform.position, m_Capsule.radius * (1.0f - advancedSettings.shellOffset), Vector3.down, out hitInfo,
((m_Capsule.height/2f) - m_Capsule.radius) + advancedSettings.groundCheckDistance, Physics.AllLayers, QueryTriggerInteraction.Ignore))
m_IsGrounded = true;
m_GroundContactNormal = hitInfo.normal;
else
m_IsGrounded = false;
m_GroundContactNormal = Vector3.up;
if (!m_PreviouslyGrounded && m_IsGrounded && m_Jumping)
m_Jumping = false;
MouseLook.cs:
public class MouseLook
public float XSensitivity = 2f;
public float YSensitivity = 2f;
public bool clampVerticalRotation = true;
public float MinimumX = -90F;
public float MaximumX = 90F;
public bool smooth;
public float smoothTime = 5f;
public bool lockCursor = true;
private Quaternion m_CharacterTargetRot;
private Quaternion m_CameraTargetRot;
private bool m_cursorIsLocked = true;
public void Init(Transform character, Transform camera)
m_CharacterTargetRot = character.localRotation;
m_CameraTargetRot = camera.localRotation;
public void LookRotation(Transform character, Transform camera)
float yRot = CrossPlatformInputManager.GetAxis("Mouse X") * XSensitivity;
float xRot = CrossPlatformInputManager.GetAxis("Mouse Y") * YSensitivity;
m_CharacterTargetRot *= Quaternion.Euler (0f, yRot, 0f);
m_CameraTargetRot *= Quaternion.Euler (-xRot, 0f, 0f);
if(clampVerticalRotation)
m_CameraTargetRot = ClampRotationAroundXAxis (m_CameraTargetRot);
if(smooth)
character.localRotation = Quaternion.Slerp (character.localRotation, m_CharacterTargetRot,
smoothTime * Time.deltaTime);
camera.localRotation = Quaternion.Slerp (camera.localRotation, m_CameraTargetRot,
smoothTime * Time.deltaTime);
else
character.localRotation = m_CharacterTargetRot;
camera.localRotation = m_CameraTargetRot;
UpdateCursorLock();
public void SetCursorLock(bool value)
lockCursor = value;
if(!lockCursor)
//we force unlock the cursor if the user disable the cursor locking helper
Cursor.lockState = CursorLockMode.None;
Cursor.visible = true;
public void UpdateCursorLock()
//if the user set "lockCursor" we check & properly lock the cursos
if (lockCursor)
InternalLockUpdate();
private void InternalLockUpdate()
if(Input.GetKeyUp(KeyCode.Escape))
m_cursorIsLocked = false;
else if(Input.GetMouseButtonUp(0))
m_cursorIsLocked = true;
if (m_cursorIsLocked)
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
else if (!m_cursorIsLocked)
Cursor.lockState = CursorLockMode.None;
Cursor.visible = true;
Quaternion ClampRotationAroundXAxis(Quaternion q)
q.x /= q.w;
q.y /= q.w;
q.z /= q.w;
q.w = 1.0f;
float angleX = 2.0f * Mathf.Rad2Deg * Mathf.Atan (q.x);
angleX = Mathf.Clamp (angleX, MinimumX, MaximumX);
q.x = Mathf.Tan (0.5f * Mathf.Deg2Rad * angleX);
return q;
将所有的墙的父物体设置为地板。
设置摄像机的位置和父物体:
运行程序:
3-5、出入口逻辑
出口用碰撞检测,新建脚本ExitControl.cs,编辑代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class ExitControl 以上是关于Unity3D开发小游戏Unity3D开发《3D迷宫》小游戏的主要内容,如果未能解决你的问题,请参考以下文章