--- title: studycase4 date: 2025-11-30 12:40:05 tags: [studycase, unity] pinned: true head: - - meta - name: description content: vitepress-theme-bluearchive studycase4 - - meta - name: keywords content: vitepress theme bluearchive studycase4 --- # 虚拟摇杆 - [世界构筑的起始篇章-编辑器](#_1-世界构筑的起始篇章-编辑器) - [光芒汇聚之所-章节目标](#_2-光芒汇聚之所-章节目标) - [光流影卷-演示视频](#_3-光流影卷-演示视频) - [初始祭坛-前置准备](#_4-初始祭坛-前置准备) - [勇者行迹录-章节任务](#_5-勇者行迹录-章节任务) - [异闻录-FAQ](#_6-异闻录-faq) ## 1.世界构筑的起始篇章-编辑器 - **Unity 2022.3.62f2**: - **Visual Studio 2022** ## 2.光芒汇聚之所-章节目标 - **虚拟摇杆和按钮** - **In game Debug** - **打包APK** ## 3.光流影卷-演示视频 ## 4.初始祭坛-前置准备 ### 导入[资源](/resources/studycase4/case4.zip)(摇杆图标、Debug工具) ### 导入InputSystem的OnScreen示例 ## 5.勇者行迹录-章节任务 ### 5.1 创建StudyCase4 - 复制场景和脚本改名为studycase4 - 修改控制器和道具的命名空间 ### 5.2 IngameDebugConsole - 把IngameDebugConsole预制体拖入场景 - 在Resources文件夹下创建材质Line,Shader选择Sprite-Unlit-Default ### 5.3 虚拟摇杆创建 - 全选mobile-controls-1下的Texture - 切换到sprite(2d and ui) - 创建一个canvas,再创建一个image,命名为leftBg,sprite使用joystick_circle_pad_a,设置大小位置和锚点 - 创建一个image,命名为leftStick,透明度0,设置大小位置和锚点,挂载On-Screen-Stick - 设置 `MovementRange` 为200,设置 `ControlPath` 为LeftStick - 复制一个leftBg改名为rightBg,设置大小位置和锚点 - 复制一个leftStick改名为rightStick,设置`ControlPath` 为RightStick - 添加输入事件,Move添加LeftStick,创建Look,添加Detla和RightStick - 设置虚拟相机参数移动速度从300调到100,限制角度0到70 - 修改虚拟相机输入 ### 5.4 Player脚本和设置 - 修改`Assets\Scripts\studycase4` 下脚本 `ThirdCharacterController.cs` ```csharp using UnityEngine; using UnityEngine.InputSystem; using Cinemachine; namespace StudyCase4 { public enum PickMode { RayCast, Mouse } public class ThirdCharacterController : MonoBehaviour { [Header("MoveSettings")] CharacterController characterController; InputActionAsset inputAction; Animator animator; Transform forward; Transform model; CinemachineVirtualCamera vCam; public float moveSpeed = 5f; public float jumpSpeed = 2f; public float turnSpeed = 10f; public float gravity = 10f; Vector3 moveDir; Vector2 moveInput; [Header("PickSettings")] public PickMode pickMode = PickMode.RayCast; public CursorLockMode cursorLock; public bool useDrawRay = true; public float maxDistance = 5f; public LayerMask layerMask; public float itemsRotateSpeed = 120f; public float minCameraDistance = 2f; public float maxCameraDistance = 10f; public float cameraZoomSpeed = 0.1f; [HideInInspector] public Transform itemRoot; Item selectItem; LineRenderer lineRenderer; CinemachineFramingTransposer framingTransposer; private void Awake() { Cursor.lockState = cursorLock; characterController = GetComponent(); forward = transform.Find("Forward"); model = transform.Find("Model"); animator = model.GetComponentInChildren(); vCam = transform.Find("Virtual Camera").GetComponent(); framingTransposer = vCam.GetCinemachineComponent(); inputAction = Resources.Load("PlayerInputActions"); inputAction.FindAction("Move").started += OnMove; inputAction.FindAction("Move").performed += OnMove; inputAction.FindAction("Move").canceled += OnMove; inputAction.FindAction("Jump").performed += OnJump; inputAction.FindAction("Pick").performed += OnPickUp; inputAction.FindAction("Zoom").started += OnZoom; inputAction.FindAction("Zoom").performed += OnZoom; inputAction.FindAction("Zoom").canceled += OnZoom; inputAction.Enable(); itemRoot = transform.Find("ItemRoot"); if (lineRenderer == null) { lineRenderer = gameObject.AddComponent(); lineRenderer.material = Resources.Load("Line"); lineRenderer.useWorldSpace = true; } layerMask = LayerMask.GetMask("Item"); //开启多点触控 Input.multiTouchEnabled = true; } private void Update() { Move(); CheckRayHit(); ItemsRotate(); } public void OnMove(InputAction.CallbackContext context) { if (characterController.isGrounded) moveInput = context.ReadValue(); else moveInput = Vector2.zero; } public void OnJump(InputAction.CallbackContext context) { if (context.performed && characterController.isGrounded) { moveDir.y = jumpSpeed; } } //增加控制相机距离的方法 public void OnZoom(InputAction.CallbackContext context) { float scrollValue = context.ReadValue(); float newDistance = framingTransposer.m_CameraDistance + (scrollValue * cameraZoomSpeed * 0.01f); framingTransposer.m_CameraDistance = Mathf.Clamp(newDistance, minCameraDistance, maxCameraDistance); } void Move() { moveDir = new Vector3(moveInput.x, moveDir.y, moveInput.y); forward.eulerAngles = new Vector3(0, vCam.transform.eulerAngles.y, 0); moveDir = forward.TransformDirection(moveDir); if (moveInput != Vector2.zero) { animator.SetBool("Move", true); Quaternion target = Quaternion.LookRotation(new Vector3(moveDir.x, 0, moveDir.z)); model.rotation = Quaternion.Slerp(model.rotation, target, turnSpeed * Time.deltaTime); } else animator.SetBool("Move", false); if (!characterController.isGrounded) moveDir.y -= gravity * Time.deltaTime; characterController.Move(moveDir * moveSpeed * Time.deltaTime); } #region PickUp public void OnPickUp(InputAction.CallbackContext context) { if (pickMode != PickMode.RayCast) return; if (selectItem != null) selectItem.PickUp(itemRoot); selectItem = null; } void CheckRayHit() { lineRenderer.enabled = useDrawRay; if (pickMode != PickMode.RayCast) { useDrawRay = false; return; } Ray ray = new Ray(model.position, model.forward); RaycastHit hit; if (Physics.Raycast(ray, out hit, maxDistance, layerMask)) { DrawRay(model.position, hit.point, new Color(0, 1, 1)); Item curItem = hit.transform.GetComponent(); if (curItem != null && curItem != selectItem) { if (selectItem != null) selectItem.UnSelect(); selectItem = curItem; selectItem.Select(); } } else { DrawRay(model.position, model.position+ model.forward* maxDistance, new Color(1, 1, 1)); if (selectItem != null) selectItem.UnSelect(); selectItem = null; } } void DrawRay(Vector3 startPos, Vector3 endPos, Color color) { lineRenderer.startColor = lineRenderer.endColor = color; lineRenderer.startWidth = lineRenderer.endWidth = 0.1f; lineRenderer.positionCount = 2; lineRenderer.SetPosition(0, startPos); lineRenderer.SetPosition(1, endPos); lineRenderer.enabled = true; } void ItemsRotate() { itemRoot.Rotate(0,itemsRotateSpeed*Time.deltaTime, 0); } public void SetItems() { float radius = 1.5f; float angleStep = 360 / itemRoot.childCount; for (int i = 0; i < itemRoot.childCount; i++) { Transform item = itemRoot.GetChild(i); float angle = angleStep * i; Quaternion rot = Quaternion.Euler(0, angle, 0); item.localPosition = rot * itemRoot.forward * radius; item.localScale = Vector3.one * 0.1f; } } #endregion } } ``` ### 5.5打包并测试 - 玩家移除case3的脚本,挂载case4脚本 - BuildSettings切换到Android平台 - 打包apk - 在手机上安装和运行 ## 6.异闻录-FAQ