Unity 虚拟美术馆漫游系统

虚拟美术馆项目在Virtual art space Volume下,Demo是场景展示

Player脚本

在脚本中通过chracter controller 控制移动

​​​​​​​​​​​​​​​​​​​​​

代码依靠 CharacterController + 相机朝向,实现跟着镜头方向走(WASD 前后左右)。


1. 获取输入方向

csharp 复制代码
float horizontal = Input.GetAxis("Horizontal"); // A/D、左右箭头:-1 ~ 1
float vertical = Input.GetAxis("Vertical");     // W/S、上下箭头:-1 ~ 1
  • Horizontal:左右轴,A=-1、D=1、无按键=0
  • Vertical:前后轴,W=1、S=-1、无按键=0
    这一步拿到玩家按键的偏移值

2. 对齐相机方向(关键:跟着镜头走)

csharp 复制代码
Vector3 cameraForward = cameraTransform.forward;
cameraForward.y = 0f;       // 去掉上下俯仰,只保留水平方向
cameraForward.Normalize();  // 单位化,保证移动速度均匀

Vector3 cameraRight = cameraTransform.right;
cameraRight.y = 0f;
cameraRight.Normalize();
  • cameraTransform.forward:相机正前方
  • y 置0 = 忽略高低,只算水平面,不会往天上/地下走
  • Normalize():把方向向量长度固定为1,避免斜着走速度变快。

3. 合成最终移动方向

csharp 复制代码
Vector3 move = cameraForward * vertical + cameraRight * horizontal;

公式理解:

  • 相机前方向 × 前后按键(W/S) = 相对镜头前后
  • 相机右方向 × 左右按键(A/D) = 相对镜头左右
    两者相加,得到基于相机视角的整体移动方向

4. 执行移动(核心运动函数)

csharp 复制代码
controller.SimpleMove(move * moveSpeed);
  • move * moveSpeed:方向 × 移动速度,算出最终移动矢量
  • CharacterController.SimpleMove()
    自带重力、碰撞检测,直接让角色沿目标方向移动,不用自己算位移。
csharp 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

// 相机控制脚本:实现第三人称跟随、鼠标旋转视角、滚轮缩放、点击交互
public class PlayerCamera : MonoBehaviour
{
    [Header("=== 相机跟随目标 ===")]
    public Transform playerTarget;         // 要跟随的玩家对象

    [Header("=== 鼠标视角控制 ===")]
    public float mouseSensitivity = 2f;   // 鼠标灵敏度
    public float minYAngle = -20f;        // 视角向下最大俯角
    public float maxYAngle = 80f;         // 视角向上最大仰角
    public float targetHeight = 1.5f;     // 相机看向玩家的高度(头顶/胸口)
    
    private float mouseX;                 // 存储鼠标左右旋转值(水平)
    private float mouseY;                 // 存储鼠标上下旋转值(垂直)

    [Header("=== 相机距离(缩放)===")]
    public float currentDistance = 3f;    // 当前相机与玩家的距离
    private float zoomSpeed = 2f;         // 滚轮缩放速度
    private float minDistance = 1f;       // 最近缩放距离
    private float maxDistance = 5f;       // 最远缩放距离

    [Header("=== UI 交互对象 ===")]
    public GameObject QuestionsCanvas;    // 问题UI面板
    public GameObject DeepSeekCanvas;     // 对话UI面板
    public GameObject DrawCanvas;         // 绘画UI面板
    
    private CanvasShowArtMessage artUI;  // 显示艺术信息的UI脚本引用


    void Start()
    {
        // 1. 如果没有手动指定玩家,自动通过标签寻找Player
        if (playerTarget == null)
        {
            GameObject player = GameObject.FindGameObjectWithTag("Player");
            if (player != null)
            {
                playerTarget = player.transform;
            }
        }
        
        // 2. 游戏开始时锁定鼠标,隐藏光标(FPS/第三人称常用)
        Cursor.lockState = CursorLockMode.Locked;
        Cursor.visible = false;
        
        // 3. 找到场景中的艺术信息UI脚本
        artUI = FindObjectOfType<CanvasShowArtMessage>();
    }

    // LateUpdate:所有Update执行完后再执行,保证相机跟随更平滑,不卡顿
    void LateUpdate()
    {
        // 没有跟随目标直接退出,防止报错
        if (playerTarget == null) return;
        
        // 处理鼠标显示/隐藏(LeftAlt切换)
        HandleCursorLock();
        
        // 只有在光标隐藏时,才允许旋转视角
        if (!Cursor.visible)
        {
            HandleMouseInput();
        }
        
        // 鼠标左键点击交互
        HandleClick();
        // 鼠标滚轮缩放
        HandleZoom();
        // 更新相机位置和角度
        UpdateCameraPosition();
    }
    
    // 功能:鼠标左键点击物体,触发UI显示
    void HandleClick()
    {
        // 检测鼠标左键按下
        if (Input.GetMouseButtonDown(0))
        {
            // 从相机发射一条射线到鼠标点击的位置
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit;
            
            // 如果射线碰到了物体
            if (Physics.Raycast(ray, out hit))
            {
                if (artUI != null)
                {
                    // 根据点击物体的名称,打开对应的UI面板
                    if(hit.collider.gameObject.name == "WorldQuestionsCanvas")
                    {
                        QuestionsCanvas.SetActive(true);
                    }
                    else if(hit.collider.gameObject.name == "WorldDeepSeekCanvas")
                    {
                        DeepSeekCanvas.SetActive(true);
                    }
                    else if(hit.collider.gameObject.name == "WorldDrawCanvas")
                    {
                        DrawCanvas.SetActive(true);
                    }
                    // 点击其他物体 → 显示对应的艺术介绍
                    else
                    {
                        artUI.ShowArtByName(hit.collider.gameObject.name + "Art");
                    }
                }
            }
        }
    }
    
    // 功能:按 Left Alt 键 切换鼠标显示/隐藏
    void HandleCursorLock()
    {
        if (Input.GetKeyDown(KeyCode.LeftAlt))
        {
            if (Cursor.visible)
            {
                // 隐藏光标 + 锁定到屏幕中心
                Cursor.lockState = CursorLockMode.Locked;
                Cursor.visible = false;
            }
            else
            {
                // 显示光标 + 解锁
                Cursor.lockState = CursorLockMode.Locked;
                Cursor.visible = true;
            }
        }
    }
    
    // 功能:获取鼠标输入,计算旋转角度
    void HandleMouseInput()
    {
        // 水平旋转:鼠标左右移动 累加
        mouseX += Input.GetAxis("Mouse X") * mouseSensitivity;
        // 垂直旋转:鼠标上下移动 累加(减号=反转Y轴,符合正常操作习惯)
        mouseY -= Input.GetAxis("Mouse Y") * mouseSensitivity;
        
        // 限制垂直视角角度,防止相机翻到角色底下或天上
        mouseY = Mathf.Clamp(mouseY, minYAngle, maxYAngle);
    }
    
    // 功能:鼠标滚轮控制相机远近(缩放)
    void HandleZoom()
    {
        // 获取滚轮输入
        float scroll = Input.GetAxis("Mouse ScrollWheel");
        // 调整相机距离
        currentDistance -= scroll * zoomSpeed;
        // 限制距离在最大/最小范围内
        currentDistance = Mathf.Clamp(currentDistance, minDistance, maxDistance);
    }
    
    // 核心功能:计算相机最终位置 + 旋转,让相机环绕玩家
    void UpdateCameraPosition()
    {
        // 相机跟随的目标点(玩家位置 + 向上偏移高度)
        Vector3 targetPosition = playerTarget.position + Vector3.up * targetHeight;
        
        // 将鼠标的X、Y值转为旋转角度
        Quaternion rotation = Quaternion.Euler(mouseY, mouseX, 0);
        
        // 相机向后移动的距离(沿着相机自身的Z轴后退)
        Vector3 negDistance = new Vector3(0.0f, 0.0f, -currentDistance);
        
        // 计算相机最终位置:目标点 + 旋转后的后退位置
        Vector3 desiredPosition = targetPosition + rotation * negDistance;
        
        // 应用旋转和位置 → 相机完成跟随+视角旋转
        transform.rotation = rotation;
        transform.position = desiredPosition;
    }
}

game这里固定为1920*1080 。

角色动画

Player中有了角色动画资源,如何使用?

播放行走动作

在Player脚本中使用角色动画资源

定义动画组件
csharp 复制代码
private Animation animation1;
  • 这是 Unity 旧版动画系统 Animation 组件
  • 角色身上已经挂载了这个组件,并放入:
    • 走路动画(名字叫 walk
    • 待机动画(名字叫 wait

代码在哪里初始化动画?

让脚本找到角色身上的 Animation 组件,以后可以用代码控制它。

csharp 复制代码
void Start()
{
    animation1 = GetComponent<Animation>(); 
}

核心:如何判断播放什么动画?

代码用 move移动量(move前面已经解释过,是一个Vector3方向向量) 来判断角色是在走还是站着, magnitude是模长:

csharp 复制代码
if (move.magnitude > 0.1f)  // 如果正在移动
{
    animation1.Play("walk"); // 播放走路动画
}
else                       // 如果没移动
{
    animation1.Play("wait"); // 播放待机动画
}

最关键的一句:动画是怎么播放的?

播放 Animation 组件里,名字叫 walk 的动画片段。

csharp 复制代码
animation1.Play("walk");

同理,播放名字叫 wait 的动画。

csharp 复制代码
animation1.Play("wait");

当游戏运行时,按住左ALT键,可以显示鼠标。

相关推荐
十贺19 小时前
【Unity开发字典】序列化基类
unity·c#·tcp
玄米乌龙茶1231 天前
LLM成长笔记(十三):系统设计与项目实战
笔记·unity·游戏引擎
_Evan_Yao2 天前
游戏和编程两不误:用Unity做一个简单小游戏
后端·游戏·unity·游戏引擎
冰凌糕2 天前
Unity3D Shader 渲染状态详解
unity
郝学胜-神的一滴2 天前
[简化版 GAMES 101] 计算机图形学 10:反走样与深度缓冲核心解析
c++·unity·godot·图形渲染·three.js·unreal engine·opengl
星幻元宇VR3 天前
VR消防安全行走平台打造真实火灾逃生体验
科技·学习·安全·vr·虚拟现实
技术探讨者3 天前
极境导表工具 —— 让配置数据成为游戏开发的效率引擎
unity·编辑器·ai编程·游戏策划
游乐码3 天前
Unity基础(六)小案例
游戏·unity·游戏引擎
星幻元宇VR3 天前
VR国防教育学习机:沉浸式国防教育新模式
科技·学习·安全·vr·虚拟现实