Unity记录6.4-动作-移动系统优化

汇总:Unity 记录
开源地址:asd123pwj/asdGame

摘要:使用派生类,对移动系统优化。

第二次优化-2023/10/05

  • 目前的这个感觉还行,不过暂时还没和tags合起来。
  • 大概是实现一个基类,然后子类继承。
    • 把一些通用判断、状态和执行都写在基类里,然后开放几个实现接口给子类。
    • 子类就是一些详细的动作,也就是独特实现的地方。
    • 再用一个字典<string, ClassBase>来记录所有动作。
  • 在实例化动作时,需要设定冷却时间,间隔时间,使用次数,持续时间。
    • 冷却时间,动作执行后,当前动作等待一段时间才可执行。
    • 间隔时间,动作执行后,所有动作等待一小段时间才可执行,避免一次按键把多跳全部用完。
    • 使用次数,用一次少一次,达到某条件(如落地)则恢复,例如多跳。
    • 持续时间,动作执行后,在这段时间内可用,否则重置,例如飞行一段时间。
  • 在实现飞行的时候,本来是想作用一个力,抵消重力,但没成功。
    • 向上施力mass*gravityScale还是会缓慢下降,再*1.25左右能勉强悬浮,但数值持续变化,且方向不同。猜测可能是精度问题。
    • 以及物理引擎的gravity,是9.81,但是这个数值和所受重力差的太多了,哪怕是1/0.981都才1.02,和1.25差的有点多
    • 看了别人3D里用9.81向上施力能浮空,结果我的2D会变成朝上飞。
    • 最后还是设置了gravityScale=0来实现。

动作脚本-2023/10/05

  • 基类
using System.Collections;
using System.Collections.Generic;
using System;
using System.Threading;
using UnityEngine;
using UnityEngine.Tilemaps;
using Cysharp.Threading.Tasks;
using Unity.VisualScripting;
using UnityEditor.Search;

public class ObjectAbilMoveBase{
    // ---------- Config ----------
    public ObjectConfig _Config;
    // ---------- Init ----------
    public string _name;
    public bool _moving;
    public bool _waiting;
    public bool _cooldowning;
    public string[] _wait_move;
    public float _wait;
    public float _cooldown;
    public int _uses_init;
    // ---------- Status ----------
    public int _uses;
    public float _duration;

    public ObjectAbilMoveBase(ObjectConfig config, float cooldown, float wait, int uses, float duration){
        // ---------- Config ----------
        _Config = config;
        // ---------- Init ----------
        _wait = wait;
        _cooldown = cooldown;
        _uses_init = uses;
        _duration = duration;
        _reset();
    }

    public virtual bool _check(InputInfo input) { return true; }
    public virtual void _act_move(InputInfo input) { }
    public virtual void _act_done() { }
    public virtual void _set_moving_status(bool isStart) { }

    // ---------- Action ----------
    public bool _act(InputInfo input){
        if (check() && _check(input)){
            init().Forget();
            _act_move(input);
            act_done();
            _act_done();
            return true;
        }
        return false;
    }

    public async UniTaskVoid _act_wait(float time){
        _waiting = true;
        await UniTask.Delay(TimeSpan.FromSeconds(time));
        _waiting = false;
    }

    bool check() { 
        if (_waiting) return false;
        if (_cooldowning && !_moving) return false;
        if (_uses == 0) return false;
        return true; 
    }

    void act_done() { 
        if (_uses > 0) _uses--;
    }

    async UniTaskVoid init(){
        _moving = true;
        _set_moving_status(true);
        if (_duration < 0) return;
        if (_duration > 0) await UniTask.Delay(TimeSpan.FromSeconds(_duration));
        _moving = false;
        _set_moving_status(false);
        if (_cooldown <= 0) return;
        _cooldowning = true;
        await UniTask.Delay(TimeSpan.FromSeconds(_cooldown));
        _cooldowning = false;
    }

    // ---------- Status ----------
    public void _reset() {
        _uses = _uses_init;
        _waiting = false;
        _cooldowning = false;
    }

    // ---------- Base ----------
    public void _act_force(Vector2 force, bool isInstant=false){
        if (!isInstant) { // such as player move
            Vector2 final_speed = new() {
                x = restrict_value_by_ori_max_change(_Config._rb.velocity.x, _Config._max_move_speed.x, force.x),
                y = restrict_value_by_ori_max_change(_Config._rb.velocity.y, _Config._max_move_speed.y, force.y)
            };
            Vector2 final_force = new(){
                x = (final_speed.x - _Config._rb.velocity.x) * _Config._rb.mass / Time.fixedDeltaTime,
                y = (final_speed.y - _Config._rb.velocity.y) * _Config._rb.mass / Time.fixedDeltaTime
            };
            _Config._rb.AddForce(final_force, ForceMode2D.Force);
        } else {// such as player jump or be blown up
            _Config._rb.AddForce(force, ForceMode2D.Impulse);
        }
    }

    float restrict_value_by_ori_max_change(float v_ori, float v_max, float v_ch){
        if (Mathf.Abs(v_ori + v_ch) <= v_max) return v_ori + v_ch;
        int dir_ori = v_ori > 0 ? 1 : -1;
        if (v_ori * dir_ori > v_max) return (v_ch * dir_ori >= 0) ? v_ori : v_ori + v_ch / 10;
        else return dir_ori * v_max;
    }
}
  • 蹬墙跳,因为额外需要判断是否在墙上,因此重写_check()
public class ObjectAbilMoveJumpWall: ObjectAbilMoveBase{
    public ObjectAbilMoveJumpWall(ObjectConfig config, float cooldown, float wait, int uses): this(config, cooldown, wait, uses, -1){}
    public ObjectAbilMoveJumpWall(ObjectConfig config, float cooldown, float wait, float duration): this(config, cooldown, wait, -1, duration){}
    public ObjectAbilMoveJumpWall(ObjectConfig config, float cooldown, float wait, int uses, float duration): base(config, cooldown, wait, uses, duration){
        _name = "jump wall";
        _wait_move = new string[]{"walk", "jump", "jump wall"};
    }

    public override bool _check(InputInfo input) { 
        if (!_Config._StatusContact._onWall) return false;
        return true; 
    }

    public override void _act_move(InputInfo input) { 
        _act_force(new(- input.x_dir * _Config._move_force.x, input.y_dir * _Config._move_force.y), true);
    }
}
  • 飞行,因为需要设置飞行状态,所以重写_set_moving_status()
public class ObjectAbilMoveFly: ObjectAbilMoveBase{
    public ObjectAbilMoveFly(ObjectConfig config, float cooldown, float wait, int uses): this(config, cooldown, wait, uses, -1){}
    public ObjectAbilMoveFly(ObjectConfig config, float cooldown, float wait, float duration): this(config, cooldown, wait, -1, duration){}
    public ObjectAbilMoveFly(ObjectConfig config, float cooldown, float wait, int uses, float duration): base(config, cooldown, wait, uses, duration){
        _name = "fly";
        _wait_move = new string[]{ "move", "jump", "jump wall", "fly" };
    }

    public override void _act_move(InputInfo input) { 
        _act_force(new(input.x * _Config._move_force.x, input.y * _Config._move_force.y));
    }

    public override void _set_moving_status(bool isStart) {
        _Config._AttrMoveFloat._act_float(isStart);
    }
}

技能恢复-2023/10/05

  • 落地时恢复技能。
void init_trigger(){
    _Config._Contact._actions_float2Ground.Add(_on_float2Ground);
}

void _on_float2Ground(){
    _Config._Move._moves["jump"]._reset();
    _Config._Move._moves["jump wall"]._reset();
    _Config._Move._moves["fly"]._reset();
}

执行-2023/10/05

  • 调用时,令相关运动等待。
    • 例如跳跃后一小段时间不能再次跳跃,不能蹬墙跳,但可以移动。
    • 感觉直接全部等待,跳跃后一小段时间不能移动也很合理。
  • 我把相关运动改成全部运动了,不过这里代码没改,反正小改动。
    • 即所有设计到_wait_move的地方都取消了,或换成了全部动作。
public Dictionary<string, ObjectAbilMoveBase> _moves = new();

public void init(){
    _moves.Add("walk", new ObjectAbilMoveWalk(_Config, 0, 0, uses:-1));
    _moves.Add("jump", new ObjectAbilMoveJump(_Config, 0, 0.25f, uses:3));
    _moves.Add("jump wall", new ObjectAbilMoveJumpWall(_Config, 0, 0.25f, uses:-1));
    _moves.Add("fly", new ObjectAbilMoveFly(_Config, 5, 0.25f, duration:5f));
}

public bool _walk(InputInfo input) { return move(input, "walk"); }
public bool _jump(InputInfo input) { return move(input, "jump"); }
public bool _jump_wall(InputInfo input) { return move(input, "jump wall"); }
public bool _fly(InputInfo input) { return move(input, "fly"); }
bool move(InputInfo input, string movement){
    if (_moves[movement]._act(input)){
        foreach (var mov in _moves[movement]._wait_move){
            if (_moves.ContainsKey(mov))
                _moves[mov]._act_wait(_moves[movement]._wait).Forget();
        }
        return true;
    }
    return false;
}

调用-2023/10/05

public void _horizontal(InputInfo input_info){
    if (_Config._Move._walk(input_info)) return;
    if (_Config._Move._fly(input_info)) return;
}

public void _vertical(InputInfo input_info){
    if (_Config._Move._jump(input_info)) return;
    if (_Config._Move._jump_wall(input_info)) return;
    if (_Config._Move._fly(input_info)) return;
}

第一次优化-2023/10/04

  • 所以的动作都经过判断可用,执行,到结束。
    • 但是动作很难保持一致的流程,有一些特殊的,比如蹬墙比走和跳要多一个在墙上的判断。
  • 所以这次的优化又放弃了,虽然比之前的简洁了许多,但是动作一多肯定又是个问题。

执行脚本-2023/10/04

bool move(InputInfo input_info, string movement, _input_action move_action){
    if (_Config._StatusMove._check_can_move(movement)){
        move_action(input_info);
        foreach (var mov in _Config._StatusMove._movements_info[movement].cooldown_movement)
            _Config._StatusMove._use_move(mov);
        return true;
    }
    return false;
}
public bool _walk(InputInfo input_info) { return move(input_info, "walk", walk); }
public bool _jump(InputInfo input_info) { return move(input_info, "jump", jump); }
public bool _jump_wall(InputInfo input_info) { return move(input_info, "jump wall", jump_wall); }

// ---------- Move Action ----------
void walk(InputInfo input_info) { add_force(new(input_info.x * _Config._move_force.x, 0)); }
void jump(InputInfo input_info) { add_force(new(0, input_info.y_dir * _Config._move_force.y), true);}
void jump_wall(InputInfo input_info) { add_force(new(-input_info.x_dir * _Config._move_force.x * 2, input_info.y_dir * _Config._move_force.y * 2), true);}

状态脚本-2023/10/04

using System.Collections;
using System.Collections.Generic;
using System;
using System.Threading;
using UnityEngine;
using UnityEngine.Tilemaps;
using Cysharp.Threading.Tasks;
using Unity.VisualScripting;
using UnityEditor.Search;

public struct MovementInfo{
    public int times;
    public bool onCooldown;
    public float cooldown_time;
    public List<string> cooldown_movement;
}

public class ObjectStatusMove{
    // ---------- Config ----------
    ObjectConfig _Config;

    // ---------- Status ----------
    public bool _onFly;

    public bool _moving { get {return check_moving("moving"); } }
    public bool _movingX { get {return check_moving("x"); } }
    public bool _movingY { get {return check_moving("y"); } }
    public bool _movingUp { get {return check_moving("up"); } }
    public bool _movingDown { get {return check_moving("down"); } }
    public bool _movingLeft { get {return check_moving("left"); } }
    public bool _movingRight { get {return check_moving("right"); } }
    // ---------- Init ----------
    Dictionary<string, MovementInfo> _init_movements_info = new();
    // ---------- Internal ----------
    public Dictionary<string, MovementInfo> _movements_info = new();

    public ObjectStatusMove(ObjectConfig config){
        // ---------- Config ----------
        _Config = config;
        _Config._StatusMove = this;
        // ---------- Init ----------
        init();
        init_status();
        init_trigger();
    }

    void init(){
        _init_movements_info.Add("walk", new() { times = -1, cooldown_movement = new(){ } });
        _init_movements_info.Add("jump", new() { times = 3, cooldown_time = 0.25f, cooldown_movement =new() { "jump", "jump wall" } });
        _init_movements_info.Add("jump wall", new() { times = -1, cooldown_time = 0.25f, cooldown_movement = new() { "walk", "jump", "jump wall" } });
        _init_movements_info.Add("fly", new() { times = -1, cooldown_time = 0.25f, cooldown_movement = new() { "fly", "walk", "jump", "jump wall" } });
    }

    void init_status(string movement="all"){
        if (movement == "all") _movements_info = new(_init_movements_info);
        else{
            _movements_info[movement] = _init_movements_info[movement];
        }
    }

    void init_trigger(){
        _Config._Contact._actions_float2Ground.Add(_onFloat2Ground);
    }

    public void _use_move(string movement) {
        var movement_info = _movements_info[movement];
        if (movement_info.times > 0) movement_info.times--;
        _movements_info[movement] = movement_info;
        cooldown_move(movement).Forget();
    }

    public bool _check_can_move(string movement){
        if (_movements_info[movement].onCooldown) return false;
        if (_movements_info[movement].times == 0) return false;
        if (movement == "jump wall" && !_Config._StatusContact._onWall) return false;
        return true;
    }

    bool check_moving(string direction){
        if (direction == "moving") return _Config._rb.velocity.magnitude != 0;
        else if (direction == "x") return _Config._rb.velocity.x != 0;
        else if (direction == "y") return _Config._rb.velocity.y != 0;
        else if (direction == "up") return _Config._rb.velocity.y > 0;
        else if (direction == "down") return _Config._rb.velocity.y < 0;
        else if (direction == "left") return _Config._rb.velocity.y < 0;
        else if (direction == "right") return _Config._rb.velocity.x > 0;
        return false;
    }

    async UniTaskVoid cooldown_move(string movement){
        var movement_info = _movements_info[movement];
        movement_info.onCooldown = true;
        _movements_info[movement] = movement_info;
        await UniTask.Delay(TimeSpan.FromSeconds(movement_info.cooldown_time));
        movement_info = _movements_info[movement];
        movement_info.onCooldown = false;
        _movements_info[movement] = movement_info;
    }

    void _onFloat2Ground(){
        init_status("jump");
        init_status("jump wall");
    }

}

调用行为-2023/10/04

public void _horizontal(InputInfo input_info){
    _Config._Move._walk(input_info);
}

public void _vertical(InputInfo input_info){
    if (_Config._Move._jump(input_info)) return;
    if (_Config._Move._jump_wall(input_info)) return;
}

版权声明:
作者:MWHLS
链接:https://mwhls.top/4978.html
来源:无镣之涯
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
打赏
< <上一篇
下一篇>>
文章目录
关闭
目 录