<四>3D角色的移动和转向

这里只记录一般角色移动控制。比如摇杆输入。

移动控制

1.在触摸屏幕时,把摇杆移动到触摸位置。
2.在触摸移动时移动标点,标点要限制在摇杆范围内
把标点和摇杆范围(半径)的比值作为摇杆输入系数,通过这个移动系数来控制角色的移动,比值大就移动的快一些,比值小就移动的慢一些。

import { _decorator, CCFloat, Component, EventTouch, Input, input, Node, v3, Vec3 } from 'cc';
import { _gameData } from './GameMgr';
const { ccclass, property } = _decorator;

@ccclass('JoyStick')
export class JoyStick extends Component {
    @property(Node)
    stick_bg: Node = null;
    @property(Node)
    nail: Node = null;
    @property({ type: CCFloat })
    radius: number = 0;

    start() {
        this.stick_bg.active = false;
        input.on(Input.EventType.TOUCH_START, this.onTouchStart, this);
        input.on(Input.EventType.TOUCH_MOVE, this.onTouchMove, this);
        input.on(Input.EventType.TOUCH_END, this.onTouchEnd, this);
        input.on(Input.EventType.TOUCH_CANCEL, this.onTouchEnd, this);
    }
    onTouchStart(event: EventTouch) {
        //移动摇杆到触摸位置
        let touch_point = event.getUILocation();
        this.stick_bg.active = true;
        this.stick_bg.setWorldPosition(touch_point.x, touch_point.y, 0);
    }
    onTouchMove(event: EventTouch) {
        let touch_point = event.getUILocation();
        //把世界坐标转换为stick_bg下的本地坐标
        let local_point: Vec3 = v3();
        this.stick_bg.inverseTransformPoint(local_point, v3(touch_point.x, touch_point.y, 0));
        //计算local_point的长度
        let length = local_point.length();
        //如果长度大于半径,则限制长度为半径
        if (length >= this.radius) {
            local_point.normalize().multiplyScalar(this.radius);
        };
        //设置nail的位置
        this.nail.setPosition(local_point.x, local_point.y, 0);
        //存储(移动)摇杆系数
        _gameData.stick_input_v = local_point.y / this.radius;
        _gameData.stick_input_h = local_point.x / this.radius;
    }
    onTouchEnd(event: EventTouch) {
        this.stick_bg.active = false;
        this.nail.setPosition(0, 0, 0);
        _gameData.stick_input_v = 0;
        _gameData.stick_input_h = 0;
    }

    update(deltaTime: number) {

    }
}

转向控制

import { v3, Vec3 } from "cc";

export class MathUtil {
    /**
      * 求带符号的夹角
      * @param from 起点
      * @param to 终点
      * @param axis 朝向参照(轴)
      */
    static signAngle(from: Vec3, to: Vec3, axis: Vec3): number {
        //通过Vec3.angle方法计算两个向量之间的夹角
        const angle = Vec3.angle(from, to);
        //始末向量进行一个叉乘,两个向量的叉乘的结果就是两个向量所形成的平面的法向量
        let cross = v3();
        Vec3.cross(cross, from, to);
        //判断这个向量和参照轴的向量的x,y,z轴的乘积,如果大于0,则说明这两个向量所形成的平面与axis向量所形成的平面是同侧的
        //Math.sign() 函数返回一个数字的符号, 指示数字是正数,负数还是零。
        const sign = Math.sign(cross.x * axis.x + cross.y * axis.y + cross.z * axis.z);
        //得到的符号乘以夹角,如果大于0,则返回夹角,否则返回负的夹角
        return sign * angle;
    }
}


import { _decorator, CCFloat, CCInteger, Collider, Component, EventTouch, Input, input, Node, RigidBody, SkeletalAnimation, v3, Vec3 } from 'cc';
import { MathUtil } from './MathUtil';
import { _gameData } from './GameMgr';
const { ccclass, property } = _decorator;

enum PlayerState {
    Idle1 = 'idle01',
    Run = 'run',
    Move = 'move',
}
@ccclass('PlayerCtrl')
export class PlayerCtrl extends Component {
    @property(SkeletalAnimation)
    player_ani: SkeletalAnimation = null;
    @property(CCFloat)
    move_speed: number = 1.0;
    @property(CCFloat)
    angleSpeed: number = 10;//角色旋转的线性移动速度

    rigidBody: RigidBody = null;
    collider: Collider = null;
    _isTouch: boolean = false;
    _temp_vec: Vec3 = v3();
    _cur_state: PlayerState = null
    start() {
        this.rigidBody = this.node.getComponent(RigidBody);
        this.collider = this.node.getComponent(Collider);
        this.changePlayerState(PlayerState.Idle1);
    }
    changePlayerState(state: PlayerState) {
        if (state == this._cur_state) return;
        if (state == PlayerState.Idle1) {
            this.stopMove();
        }
        this._cur_state = state;
        this.player_ani.play(state);
    }
    doMove() {
        //速度 = 方向(node.forward)*基础速度*速度因子(摇杆输入/加速减速)
        const speed = this.move_speed * v3(_gameData.stick_input_h, 0, -_gameData.stick_input_v).length();//在编辑器中看到人物显示的朝向是负z的方向,这里取一个负值
        this._temp_vec.x = this.node.forward.x * speed;
        this._temp_vec.z = this.node.forward.z * speed;
        this._temp_vec.y = 0;
        this.node.getComponent(RigidBody).setLinearVelocity(this._temp_vec);
    }
    doRotate() {
        //角色只会沿着Y轴旋转
        this._temp_vec.x = 0;
        //输入朝向和当前朝向的夹角(带符号)
        const angle = MathUtil.signAngle(this.node.forward, v3(-_gameData.stick_input_h, 0, _gameData.stick_input_v), Vec3.UP)
        this._temp_vec.y = angle * this.angleSpeed;
        this._temp_vec.z = 0;

        //设置角速度
        this.rigidBody.setAngularVelocity(this._temp_vec);
    }
    stopMove() {
        console.log('stopMove');
        this.node.getComponent(RigidBody).setLinearVelocity(Vec3.ZERO);
        this.node.getComponent(RigidBody).setAngularVelocity(Vec3.ZERO);
    }



    update(deltaTime: number) {
        if (_gameData.stick_input_h != 0 || _gameData.stick_input_v != 0) {
            this.changePlayerState(PlayerState.Run);
            this.doRotate();
            this.doMove();
        } else {
            this.changePlayerState(PlayerState.Idle1);
        }
    }
}



posted @ 2024-10-24 15:59  EricShx  阅读(45)  评论(0)    收藏  举报