Unity游戏基础-2(初识场景~项目构建)

本教程开始分享教学内容:Unity游戏基础,部分内容会包括Blender建模基础,Shader基础,动画基础等等,教程面向:建模、技术美术、游戏前端、游戏后端基础,适合美术与设计(数媒、环设、动画、产设、视传、绘画)专业、计算机(计算机科学与技术、软件工程、数媒)的同学学习。

场景是游戏或者3D程序的基础,内容在此显示,摄像机是输出场景的物体

加入3D对象(3D Object)

在层级Hierarchy里面,打开SampleSence,右键空白,选择3D对象,点击平面(Plane)

添加一个胶囊Capsule,重命名为Player,把MainCamera拖到Palyer上面,这样player旋转camera也会旋转

添加一个空物体,重命名为GroundCheck,移动到Player,并且调整位置到player胶囊体的下面(悬空),就像这样:

添加一个立方体,面朝Z轴正方向,放在胶囊体前面,摄像机放在Z轴负方向

控制角色

在Assets下新建一个文件夹,重命名为Script,我们要在里面编写脚本

新建2个脚本,重命名为CameraController和PlayerMovement,顾名思义了就是控制摄像机的和控制角色的,后期在添加UI设置时会用到本教程第三个脚本

编写脚本:(完整移动,缩放,旋转,碰撞)

cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEditor.Rendering;
using UnityEngine;


public class PlayerMovement : MonoBehaviour
{
    public float moveSpeed = 5f;
    public float rotateSpeed = 10f;
    public float jumpHeight = 1f;
    public float gravity = -9.8f;
    public float pushPower = 3f;

    public Transform groundCheck; // 将你在脚底创建的空物体拖拽到这里
    public float groundCheckRadius = 0.2f; // 检测范围的半径
    public LayerMask groundMask; // 在Inspector中设置为"Ground"层

    private CharacterController controller;
    private Vector3 playerVelocity;
    private bool isGrounded;
    private Transform cameraTransform;

    void Start()
    {
        controller = GetComponent<CharacterController>();
        cameraTransform = Camera.main.transform; // 获取主摄像机变换
    }

    void Update()
    {
        if (CameraController.isleave)
            return;

        // 检查角色是否着地
        isGrounded = Physics.CheckSphere(groundCheck.position, groundCheckRadius, groundMask);

        if (controller.isGrounded)
        {
            HandleJump();
            CheckSpeed();
        }
        HandleMovement();
    }
    void CheckSpeed()
    {
        if (Input.GetKeyDown(KeyCode.LeftShift))
        {
            moveSpeed = 15f;
        }
        else if (Input.GetKeyUp(KeyCode.LeftShift))
        {
            moveSpeed = 5f;
        }
    }

    void HandleMovement()
    {
        // 获取输入
        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");

        // 根据摄像机方向计算移动方向
        Vector3 camForward = Vector3.Scale(cameraTransform.forward, new Vector3(1, 0, 1)).normalized; // 忽略摄像机俯仰角
        Vector3 moveDirection = (camForward * vertical + cameraTransform.right * horizontal).normalized;

        // 应用移动速度
        Vector3 movement = moveDirection * moveSpeed * Time.deltaTime;
        controller.Move(movement);

        // 如果需要角色朝向移动方向,可以取消注释下面的代码
        if (moveDirection.magnitude > 0.1f)
        {
            Quaternion targetRotation = Quaternion.LookRotation(moveDirection);
            transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, rotateSpeed * Time.deltaTime);
        }

        // 应用重力
        playerVelocity.y += gravity * Time.deltaTime;
        controller.Move(playerVelocity * Time.deltaTime);
    }
    void OnControllerColliderHit(ControllerColliderHit hit)
    {
        Rigidbody hitRigidbody = hit.collider.attachedRigidbody;

        // 如果碰撞的对象没有刚体、是运动学刚体,或者被卡在下面,则不推动
        if (hitRigidbody == null || hitRigidbody.isKinematic || hit.moveDirection.y < -0.3f)
        {
            return;
        }

        // 计算推动方向(主要在X和Z轴)
        Vector3 pushDir = new Vector3(hit.moveDirection.x, 0, hit.moveDirection.z);

        // 给被碰撞的刚体一个速度来实现推动效果
        hitRigidbody.velocity = pushDir * pushPower;
    }
    void HandleJump()
    {
        if (Input.GetButtonDown("Jump") && isGrounded)
        {
            playerVelocity.y = Mathf.Sqrt(jumpHeight * -1f * gravity); // 计算跳跃速度
        }
    }

}
cs 复制代码
using System;
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEditor.Rendering;
using UnityEngine;

public class CameraController : MonoBehaviour
{
    public Transform target; // 玩家角色
    public float distance = 5.0f; // 默认相机距离
    public float minDistance = 1.5f; // 最小相机距离
    public float maxDistance = 15.0f; // 最大相机距离
    public float zoomSpeed = 2.0f; // 缩放速度
    public float rotationSpeed = 5.0f; // 旋转速度
    public float minVerticalAngle = -20.0f; // 垂直旋转最小角度
    public float maxVerticalAngle = 80.0f; // 垂直旋转最大角度
    public float collisionOffset = 0.2f; // 碰撞偏移量
    public LayerMask collisionLayers = ~0; // 碰撞检测层(默认所有层)
    public static bool isleave;

    private float currentX = 0.0f;
    private float currentY = 0.0f;
    private float targetDistance;
    private Vector3 cameraDirection;
    /// <summary>
    /// 鼠标
    /// </summary>
    private void CursorController()
    {        

        // 优先判断:只要UI界面打开,就解锁并显示鼠标

        if (ButtonsEventHandler.isopen)
        {
            Cursor.lockState = CursorLockMode.None;
            isleave = true;
            return;
        }
        

        if (Input.GetKeyDown(KeyCode.LeftAlt))
        {
            Cursor.lockState = CursorLockMode.None;
            isleave = true;
        }
        if (Input.GetKeyUp(KeyCode.LeftAlt))
        {
            //Debug.Log(ButtonsEventHandler.isopen);
            Cursor.lockState = CursorLockMode.Locked;
            isleave = false;
        }

    }

    void Start()
    {
        // 初始化当前旋转角度
        Cursor.lockState = CursorLockMode.Locked;
        Vector3 angles = transform.eulerAngles;
        currentX = angles.y;
        currentY = angles.x;
        targetDistance = distance;
        cameraDirection = (transform.position - target.position).normalized;
    }
     void Update()
    {
        CursorController();

    }
    void LateUpdate()
    {
        
        if (target == null)
            return;
        if (isleave)
            return;
        // 处理鼠标输入
        HandleInput();

        // 计算相机旋转和方向
        Quaternion rotation = Quaternion.Euler(currentY, currentX, 0);
        Vector3 direction = rotation * Vector3.back;

        // 碰撞检测和避免
        HandleCameraCollision(direction);

        // 应用相机位置和旋转
        transform.position = target.position + direction * targetDistance;
        transform.LookAt(target.position);
    }

    void HandleInput()
    {
        // 鼠标旋转控制
        //if (Input.GetMouseButton(0)) // 右键按住旋转
        //{
        currentX += Input.GetAxis("Mouse X") * rotationSpeed;
        currentY -= Input.GetAxis("Mouse Y") * rotationSpeed;
        currentY = Mathf.Clamp(currentY, minVerticalAngle, maxVerticalAngle);
        //}

        // 鼠标滚轮缩放
        float scroll = Input.GetAxis("Mouse ScrollWheel");
        if (scroll != 0)
        {
            distance -= scroll * zoomSpeed;
            distance = Mathf.Clamp(distance, minDistance, maxDistance);
        }
    }
    void HandleCameraCollision(Vector3 direction)
    {
        RaycastHit hit;
        // 定义一个小的偏移量,将射线起点从角色中心向后移动,避免与自身碰撞
        float selfAvoidanceOffset = 1f;
        Vector3 rayStartPoint = target.position + direction * selfAvoidanceOffset;
        float rayLength = distance - selfAvoidanceOffset;

        // 从调整后的起点发射射线
        if (Physics.Raycast(rayStartPoint, direction, out hit, rayLength, collisionLayers))
        {
            // 如果检测到碰撞,调整相机距离
            targetDistance = Mathf.Clamp(hit.distance - collisionOffset, minDistance, maxDistance);
        }
        else
        {
            // 没有碰撞则使用预设距离
            targetDistance = distance;
        }
    }

}

现在会处于一个报错的状态,没事,我们再来写一个脚本ButtonsEventHandler

cs 复制代码
using System;
using System.Collections;
using System.Collections.Generic;
using TMPro;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.UI;

[Serializable]
public class Config
{
    private static int SelectIndex;
    private static bool isfull;
}

public class ButtonsEventHandler : MonoBehaviour
{
    public static Config cfg;

    public  TMP_Dropdown resolutionDropdown; // 在Inspector中关联你的ResolutionDropdown
    public Toggle fullscreenToggle; // 在Inspector中关联你的FullscreenToggle
    public GameObject SeetingForm;
    /// <summary>
    /// 指示设置窗口是否开启
    /// </summary>
    public static bool isopen = false;

    private static int SelectIndex = 0;

    private static bool isfull = false;
    /// <summary>
    /// 获取所有支持的分辨率
    /// </summary>
    private static List<Resolution> sizes = new List<Resolution>();

    // Start is called before the first frame update
    void Start()
    {        
        SeetingForm.SetActive(false);
        LoadSize();
        resolutionDropdown.onValueChanged.RemoveListener(OnDropdownValueChanged);
        resolutionDropdown.onValueChanged.AddListener(OnDropdownValueChanged);

        fullscreenToggle.onValueChanged.RemoveListener(onToggleValueChanged);
        fullscreenToggle.onValueChanged.AddListener(onToggleValueChanged);
    }
    private void onToggleValueChanged(bool check)
    {
        isfull = check;
    }
    private void OnDropdownValueChanged(int newIndex)
    {
        SelectIndex = newIndex;
    }
    void LoadSize()//加载所有分辨率
    {
        resolutionDropdown.ClearOptions();//清空列表
        sizes.Clear();

        List<string> size1 = new List<string>();

        foreach (var s in Screen.resolutions)
        {
            sizes.Add(s);
            size1.Add(s.width + "x" + s.height + " " + s.refreshRateRatio);
            Debug.Log(s.width + "x" + s.height + " " + s.refreshRateRatio);
        }

        resolutionDropdown.AddOptions(size1);//全部加载分辨率
    }


    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Escape))
        {
            OnButtonPress();
        }
    }

    public void OnButtonPress()//打开或关闭设置界面,打开就加载分辨率
    {
        if (isopen)
        {
            SeetingForm.SetActive(false);
            isopen = false;
            Cursor.lockState = CursorLockMode.Locked;
            CameraController.isleave = false;
        }
        else
        {
            SeetingForm.SetActive(true);
            isopen = true;
            Cursor.lockState = CursorLockMode.None;
            CameraController.isleave = true;
            LoadSize();
        }

    }
    public void Apply()
    {

        FullScreenMode mode;
        if (isfull)
        {
            mode = FullScreenMode.FullScreenWindow;
        }
        else
        {
            mode = FullScreenMode.Windowed;
        }
        Screen.SetResolution(sizes[SelectIndex].width, sizes[SelectIndex].height, mode, sizes[SelectIndex].refreshRateRatio);
    }


    public void Cancel()
    {
        SeetingForm.SetActive(false);
        isopen = false;
    }


    public void Close()
    {
        Application.Quit();
    }



}

挂载脚本

将 PlayerMovement直接拖到Hierarchy窗口里面的Player上面,然后在Inspector窗口里发现了你的脚本

修改Plane的图层,点击Plane,在inspector里面找到图层下拉列表,添加一个Ground层

点击Player,把groundcheck空物体拖到脚本的Ground Check

设置GroundMask为Ground

设置成功的样子Inspector

player的

Plane的

挂载CameraController

把CameraController拖到Main Camera上,把目标(Target)设置为Player

为Player添加组件(Add Component),搜索Character Controller

完成!

点击上面的小三角开始调试

现在按WASD就可以移动你的胶囊体了

构建项目:Ctrl+B开始游戏(直接新建文件夹Bin)

(报错的话删除脚本里面没有使用的命名空间,通常是灰色的)

生成设置Ctrl+Shift+B,下一节我们教学分辨率自定义设置

相关推荐
yi碗汤园15 小时前
【一文了解】八大排序-冒泡排序、选择排序
开发语言·前端·算法·unity·c#·1024程序员节
嘀咕博客15 小时前
h5游戏免费下载:2048.0
游戏
Hello123网站17 小时前
h5游戏免费下载:读心术
游戏
qq_2052790518 小时前
unity PS 给图片换透明底图,去除白色底图
unity
AA陈超21 小时前
虚幻引擎5 GAS开发俯视角RPG游戏 P06-19 打开属性菜单
c++·游戏·ue5·游戏引擎·虚幻
向宇it21 小时前
【推荐100个unity插件】unity易于使用模块化设计的天空、体积云和天气系统——Enviro 3
游戏·3d·unity·c#·游戏引擎
初九之潜龙勿用1 天前
UU远程深度测评:聚焦游戏与Windows多屏场景,免费实用的远程控制选择
游戏·远程工作·远程
wanhengidc1 天前
云手机在网络游戏中的主要功能
运维·服务器·游戏·智能手机
嘀咕博客1 天前
h5游戏免费下载:小霸王游戏机
游戏·游戏机
怣疯knight1 天前
unity实现2D人物从上面踩踏敌人,敌人减血的简易方法(类似马里奥的攻击手段)
unity·游戏引擎