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迷宫》小游戏的主要内容,如果未能解决你的问题,请参考以下文章

学Unity3D游戏开发需要了解哪些内容

unity3D怎么入门?

学unity3D游戏开发需要什么基础

[Unity3D]Unity3D游戏开发之从Unity3D到Eclipse

unity3d开发2d游戏中Assetbundle有啥作用

搭建Unity3D游戏引擎开发工具系列:UI框架