PlayerController(角色移动(不需要碰撞器和刚体的方法))

本文详细介绍了一个基于Unity3D的玩家角色控制器的实现方法,包括移动、跳跃、旋转等功能,以及如何处理重力、碰撞检测和滞后的优化技巧。

using UnityEngine;

public class PlayerController : MonoBehaviour
{
    public Transform Itself;//自己本身
    public GameObject Cam;//摄像机
    public bool Gravity=true;//重力
    public bool inAir=true;//空中

    //方便移动和跳跃(外面只需要修改这里就可以移动跳跃旋转)
    public bool ForwardMove = false;
    public bool BackMove = false;
    public bool RightMove = false;
    public bool LeftMove = false;
    public bool IsJump = false;
    public Vector2 Rot = Vector2.zero;//旋转值
    public bool IsTouchPadRot = false;
    public float JumpSpeed=15f;//跳跃速度
    public float MoveSpeed=8f;//速度
    public float RotX_Speed=10f;//旋转x速度
    public float RotY_Speed=10f;//旋转y速度
    public Vector3 velocity = Vector3.zero;
    public bool IsRot = true;//是否使用旋转
    public bool IsPC_Rot = true;//旋转采用电脑或者其他设备
    public float CapsuleR=0.5f;//胶囊半径
    public float ShiftAcceleration=1.7f;//加速倍速度

    public Vector3 CapsuleP1=Vector3.zero;//胶囊1负
    public Vector3 CapsuleP2=Vector3.zero;//胶囊2正
    public bool CameraSpace=true;//相机空间
    public Vector3 ForceVelocity=Vector3.zero;//力速度
    public float StepsPerSecond = 50;//步长秒
    public float Acceleration = 50f;//加速
    public bool UseLag = false;//使用滞后
    public float LagTimeLeft;//剩余滞后时间
    public float LagTime = 0.1f;//滞后时间
    public float oldTime=0;//计时
    public static readonly int[] SearchDirVert = { 1, -1, 0, 0, 1, 1, -1, -1 };//搜索方向垂直
    public static readonly int[] SearchDirHor = { 0, 0, 1, -1, -1, 1, 1, -1 };//搜索方向水平

    private void Update()
    {
        if (Cam == null) Cam = Camera.main.transform.gameObject;
        //自身
        if (Itself == null) Itself = transform;
        //胶囊参数
        CapsuleCollider capsuleCollider = Itself.GetComponent<CapsuleCollider>();
        if (capsuleCollider != null)
        {
            CapsuleP1 = capsuleCollider.center + Vector3.up * (capsuleCollider.height / 2 - capsuleCollider.radius);
            CapsuleP2 = capsuleCollider.center - Vector3.up * (capsuleCollider.height / 2 - capsuleCollider.radius);
            CapsuleR = capsuleCollider.radius;
        }
        
        //模仿滞后
        LagTimeLeft -= Time.deltaTime;
        if (UseLag && LagTimeLeft > 0) return;
        LagTimeLeft = LagTime;

        //确定观看方向
        Vector3 lookDir;
        if (CameraSpace)
        {
            lookDir = new Vector3(Cam.transform.forward.x, Cam.transform.forward.y, Cam.transform.forward.z);
            if (Gravity) lookDir.y = 0;
        }
        else lookDir = Itself.transform.forward;

        lookDir = lookDir.normalized;
        Vector3 strafeDir = Vector3.Cross(Vector3.up, lookDir);
        //移动的
        Vector3 direction = new Vector3(0, 0, 0);
        if (Input.GetKeyDown(KeyCode.LeftShift)) { MoveSpeed *= ShiftAcceleration;}
        if ( Input.GetKeyUp(KeyCode.LeftShift)) { MoveSpeed /= ShiftAcceleration;}
        if (IsJump||Input.GetKey(KeyCode.Space) && !inAir) { velocity.y = JumpSpeed;IsJump = false; }
        if (ForwardMove||Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.UpArrow)) { direction += lookDir; }
        if (BackMove||Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.DownArrow)) { direction -= lookDir; }
        if (RightMove||Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.RightArrow)) { direction += strafeDir; }
        if (LeftMove||Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.LeftArrow)) { direction -= strafeDir; }
        if (IsRot)
        {
            if (IsPC_Rot)
            {
                Cam.transform.Rotate(new Vector3(-Input.GetAxis("Mouse Y"), 0, 0) * RotY_Speed * Time.deltaTime);
                Itself.Rotate(new Vector3(0, Input.GetAxis("Mouse X"), 0) * Time.deltaTime * RotX_Speed);
            }
            else
            {
                if (IsTouchPadRot)
                {
                     Cam.transform.Rotate(new Vector3(-Rot.y, 0, 0) * RotY_Speed * Time.deltaTime);
                     Itself.Rotate(new Vector3(0, Rot.x, 0) * Time.deltaTime * RotX_Speed);
                    //rot.x += Time.deltaTime * RotX_Speed * Rot.x;
                   // rot.y += Time.deltaTime * RotY_Speed * Rot.y;
                    //Cam.transform.rotation = Quaternion.Euler(-rot.y, 0, 0);
                    //Itself.rotation = Quaternion.Euler(0, rot.x, 0);
                    IsTouchPadRot = false;
                }
            }
        }
        direction *= MoveSpeed * 10;
        float deltaTime = Time.deltaTime; if (UseLag) deltaTime = Time.realtimeSinceStartup - oldTime;
        Itself.position = MovePlayer(Itself.position, direction, deltaTime); //不调试时使用增量时间
        oldTime = Time.realtimeSinceStartup;
    }

    public Vector3 MovePlayer(Vector3 pos, Vector3 direction, float time) //方向是控制器发送电荷的地方,速度是旧的速度
    {
        //计算迭代次数
        int numSteps = (int)(time * StepsPerSecond) + 1;
        float stepTime = time / numSteps;
        if (numSteps > StepsPerSecond / 10f) numSteps = (int)(StepsPerSecond / 10f); //如果fps太低,则断开速度

        for (int i = 0; i < numSteps; i++)
        {
            Vector3 initialPos = pos; //防止卡住
            //调节速度
            if (direction.sqrMagnitude > 0.01f) //如果移动
            {
                velocity += direction.normalized * Acceleration * stepTime;
                velocity = Vector3.ClampMagnitude(velocity, MoveSpeed);
            }
            else
            {
                Vector3 velocityModifier = velocity.normalized * Acceleration * stepTime;
                if (velocity.sqrMagnitude > velocityModifier.sqrMagnitude) velocity -= velocityModifier; //如果还在移动
                else velocity = Vector3.zero;
            }
            pos = TryMove(pos, velocity * stepTime);
            //应用重力
            if (Gravity)
            {
                ForceVelocity += Vector3.down * stepTime * 9.8f; //加速下降
                if (ForceVelocity.y * stepTime > CapsuleR) ForceVelocity.y = CapsuleR / stepTime; //极限速度
                Vector3 fallPos = pos + ForceVelocity * stepTime;
                if (!Physics.CheckSphere(fallPos + CapsuleP2, CapsuleR) && !Physics.CheckSphere(fallPos + CapsuleP1, CapsuleR)) //也许胶囊1是不必要的
                { pos = fallPos; inAir = true; } //如果脚下什么都没有掉下来
                else { ForceVelocity.y = 0; inAir = false; } //如果检测到地面,停止坠落
            }
            //检查卡住
            if (Physics.CheckSphere(pos + CapsuleP1, CapsuleR) || Physics.CheckSphere(pos + CapsuleP2, CapsuleR) || Physics.Linecast(initialPos, pos))
            {
                Debug.Log("Controller stuck(碰撞器卡住)");
                if (Physics.CheckSphere(pos + CapsuleP1, CapsuleR) || Physics.CheckSphere(pos + CapsuleP2, CapsuleR))
                {
                    Debug.Log("CharController locked(碰撞器锁定)");pos = GetOutofStuck(pos);
                }
                pos = initialPos;velocity = new Vector3(0, velocity.y+ 0.1f, 0);
            }
            //else Debug.Log("notstuck");
        }
        return pos;
    }

    public Vector3 TryMove(Vector3 pos, Vector3 moveVector)
    {
        //检查char是否可以直走
        if (!Physics.CheckSphere(pos + moveVector + CapsuleP1, CapsuleR) && !Physics.CheckSphere(pos + moveVector + CapsuleP2, CapsuleR))
        { return pos + moveVector; }

        //准备载体
        Vector3 perpHor = Vector3.Cross(moveVector, Vector3.up).normalized;
        Vector3 perpVert = Vector3.Cross(moveVector, perpHor).normalized;
        float moveDist = moveVector.magnitude;

        //如果char不能直接找到替代位置
        for (float i = 0.5f; i < 100f; i = i * 1.5f + 0.5f)
            for (int dir = 0; dir < 8; dir++)
            {
                Vector3 possibleDir = (moveVector + perpHor * SearchDirHor[dir] * i * moveDist * 0.1f + perpVert * SearchDirVert[dir] * i * moveDist * 0.1f).normalized;
                possibleDir *= Vector3.Dot(moveVector, possibleDir);
                //possibleDir = possibleDir*0.5f + possibleDir.normalized*moveDist * 0.5f;

                if (!Physics.CheckSphere(pos + possibleDir + CapsuleP1, CapsuleR) && !Physics.CheckSphere(pos + possibleDir +CapsuleP2, CapsuleR))
                { return pos + possibleDir; }
            }

        //如果找不到替代位置-返回原始位置
        return pos;
    }

    public Vector3 GetOutofStuck(Vector3 pos)
    {
        for (float dist = 0.07f; dist < 3f; dist *= 1.5f)
            for (int xi = 0; xi < 20; xi++)
                for (int zi = 0; zi < 20; zi++)
                {
                    int x = 0; int z = 0;
                    if (xi % 2 == 0) x = xi * xi; else x = -(xi + 1) * (xi + 1);
                    if (zi % 2 == 0) z = zi * zi; else z = -(zi + 1) * (zi + 1);

                    Vector3 dir = (new Vector3(x, 150, z)).normalized;
                    dir.y -= 0.5f;
                    dir = dir.normalized * dist;

                    if (!Physics.CheckSphere(pos + dir + CapsuleP1, CapsuleR) && !Physics.CheckSphere(pos + dir + CapsuleP2, CapsuleR))
                        return pos + dir;
                }

        return pos;
    }
}
 

<think>好的,我现在需要回答用户关于在Unreal Engine或Unity中PlayerControllerCharacter的初始化方法的问题。用户特别提到了UnrealUnity两个引擎,所以我应该分别针对这两个引擎进行说明,同时参考用户提供的引用内容。 首先,我需要回忆一下Unreal Engine中PlayerControllerCharacter的初始化流程。根据引用[1]引用[4],Unreal中的类通常使用C++定义,并且属性可以通过UPROPERTY宏来暴露给编辑。例如,Character可能有Health属性,而PlayerController可能需要初始化HUD,如引用[5]提到的UMGHUD更新。初始化可能涉及构造函数、BeginPlay函数,以及组件初始化。例如,在C++中,构造函数用于设置默认值,而BeginPlay用于游戏开始时的逻辑。 对于Unity,引用[3]指出脚本是组件,需要附加到游戏对象上,AwakeStart方法是初始化的重要部分。Character可能由脚本控制,而PlayerController可能通过获取组件或实例化预制体来初始化。此外,引用[2]提到Unity中使用代码来组合游戏元素,比如无限滚动背景,这可能涉及到在Start方法中初始化对象。 接下来需要分点整理UnrealUnity中的初始化步骤,确保涵盖两者的异同。在Unreal中,C++类的构造函数BeginPlay是初始化关键,而Unity使用AwakeStart。同时,要注意Unreal的蓝图系统Unity的脚本组件的区别。 然后,根据用户提供的引用,需要适当引用相关内容。例如,Unreal中的UPROPERTYUMG创建界面可以参考引用[1][5],而Unity中的脚本组件GetComponent可以参考引用[3]。 可能还需要生成相关问题,如初始化顺序、蓝图与C++的交互、常用方法比较等,确保问题覆盖用户可能感兴趣的扩展内容。 最后,确保回答结构清晰,使用正确的LaTeX格式,如行内公式用$...$,独立公式用$$...$$,但在这个问题中可能需要数学公式,所以主要关注代码块引用标识。同时检查是否使用中文,语法正确,并且引用标识正确添加。</think>在Unreal EngineUnity中,$PlayerController$$Character$的初始化方式有以下核心差异实现方法: --- ### 一、Unreal Engine中的初始化 1. **$Character$初始化** - **C++构造函数**:定义基础属性,如移动组件碰撞体 ```cpp // MyCharacter.cpp AMyCharacter::AMyCharacter() { PrimaryActorTick.bCanEverTick = true; GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f); // 引用[1]中UPROPERTY设计 } ``` - **$BeginPlay()$**:游戏开始时加载动态数据(如初始生命值) ```cpp void AMyCharacter::BeginPlay() { Super::BeginPlay(); Health = 100.f; // 引用[1]的Health属性初始化 } ``` 2. **$PlayerController$初始化** - **绑定输入**:在`SetupInputComponent`中关联按键事件 ```cpp void AMyPlayerController::SetupInputComponent() { Super::SetupInputComponent(); InputComponent->BindAction("Jump", IE_Pressed, this, &AMyPlayerController::HandleJump); } ``` - **HUD初始化**:通过`CreateWidget`加载UI(引用[5]的UMG流程) ```cpp void AMyPlayerController::BeginPlay() { UUserWidget* HUD = CreateWidget<UUserWidget>(this, HUDClass); HUD->AddToViewport(); } ``` --- ### 二、Unity中的初始化 1. **$Character$初始化** - **脚本挂载**:将`CharacterController`组件附加到GameObject ```csharp // PlayerCharacter.cs void Awake() { _controller = GetComponent<CharacterController>(); // 引用[3]的GetComponent方法 _health = 100; // 初始化属性 } ``` - **物理参数设置**:在`Start()`中配置刚体碰撞体 ```csharp void Start() { GetComponent<Rigidbody>().mass = 2.0f; } ``` 2. **$PlayerController$初始化** - **输入监听**:通过`Input System`绑定事件 ```csharp // PlayerController.cs void Start() { InputSystem.onActionTriggered += OnActionTriggered; } ``` - **UI加载**:实例化预制体并添加到画布 ```csharp void Awake() { _hud = Instantiate(hudPrefab, canvas.transform); // 引用[2]的UI组合方式 } ``` --- ### 三、关键差异对比 | 引擎 | 初始化阶段 | 典型方法 | |---------------|---------------------|---------------------------------------| | Unreal Engine | 构造函数 → BeginPlay | `UPROPERTY`配置、`SetupInputComponent` | | Unity | Awake → Start | `GetComponent`、脚本事件绑定 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值