Unity数字孪生开发笔记——轿厢基本运动实现

一、轿厢实例说明

  • 楼层信息获取----包括楼层名和在各楼层时轿厢的位置;
  • 轿厢上下移动----判断当前位置与目标点的距离,通过距离的正负来决定移动方向;
  • 对重移动----对重的移动取决于轿厢,同时与轿厢应保持速度相同和运动方向相反;
  • 开关层门与轿厢门----根据轿厢的状态以及轿厢当前位置判断是否停靠在某一层,播放动画;
  • 轿厢AI----根据情况自动调用以上各部分。

二、需求分析

1、轿厢移动

  • 定义列表变量----楼层信息
  • 轿厢运动状态判断及运行----上行、下行、停止

1>楼层信息

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 定义状态信息类,定义轿厢人员信息,
/// </summary>
public class StatusInfo : MonoBehaviour
{
    
    
    /// <summary>
    /// 当前电梯人数
    /// </summary>
    public int people = 2;

    /// <summary>
    /// 电梯可承载人数最大值
    /// </summary>
    public int maxPeople = 12;

    /// <summary>
    /// 当前正在走进或走出电梯的人数
    /// </summary>
    public int amountPeople = 2;

    /// <summary>
    /// 人员流动
    /// </summary>
    public bool MovePeople()
    {
    
    
        int currentPeople = people + amountPeople;
        //如果当前电梯没人或者没有人员流动
        if (people == 0 || currentPeople == people)
            return false;
        //如果电梯当前人数大于最大承载量
        else if (currentPeople > maxPeople)
            return false;
        else
            return true;
    }

}

2>轿厢运动

using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;

/// <summary>
/// 控制轿厢移动
/// </summary>
public class Move : MonoBehaviour
{
    
    
    public Transform[] points;
    public WayLine line;
    

    //当前路点索引
    private int currentPointIndex=0;

    public float MoveSpeed = 5;

    /// <summary>
    /// 定义轿厢运行状态
    /// </summary>
    public enum CarState
    {
    
    
        up,
        down,
        stop
    }
    public CarState carCurrentState = CarState.stop;

    /// <summary>
    /// 轿厢状态判断
    /// </summary>
    /// <param name="point">目标点坐标</param>
    public CarState CarStateJudge(Vector3 point)
    {
    
    
        if (this.transform.position.y < point.y && (decimal)Vector3.Distance(this.transform.position, point) > 0.05m)
            return carCurrentState = CarState.up;
        else if (this.transform.position.y > point.y && (decimal)Vector3.Distance(this.transform.position, point) > 0.05m)
            return carCurrentState = CarState.down;
        else
            return carCurrentState = CarState.stop;
    }

 

    /// <summary>
    /// 轿厢上行、下行、停止
    /// </summary>
    /// <param name="point">目标点</param>
    public void CarMovement()
    {
    
    
        try
        {
    
    
            //如果当前楼层的索引小于存储的楼层数
            if (currentPointIndex < points.Length)
            {
    
    
                CarStateJudge(points[currentPointIndex].position);
                switch (carCurrentState)
                {
    
    
                    case CarState.up:
                        this.transform.Translate(0, MoveSpeed * Time.deltaTime, 0);
                        break;
                    case CarState.down:
                        this.transform.Translate(0, -MoveSpeed * Time.deltaTime, 0);
                        break;
                    case CarState.stop:
                        this.transform.Translate(0, 0, 0);
                        break;
                }
            }            
        }
        catch {
    
     }
                  
    }

    /// <summary>
    /// 寻路,沿路线(Vector3[])移动
    /// </summary>
    /// <returns></returns>
    public bool Pathfinding()
    {
    
    
        //如果到达目标点(判断 当前位置 与 目标点间距 Vector3.Distance)
        //更新目标点(向下一个路点移动)
        if (points == null || currentPointIndex >= points.Length)
        {
    
    
            this.transform.Translate(0, 0, 0);
            return false;
        }
        else
        {
    
    
            //移动
            CarMovement();

            //如果到达目标点(当前位置 趋近于 目标点)
            if ((decimal)Vector3.Distance(this.transform.position, points[currentPointIndex].position) < 0.05m)
                currentPointIndex++;
            return true;//可以继续寻路
        }                
    }

}


2、对重移动

  • 对重移动与轿厢移动相反,同时要跟随轿厢移动
  • 欠缺:只能将轿厢的位置放在最高,对重的位置放在最低,后期可能会想着将这两个对象的位置时刻关联(估计有难度)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 对重移动
/// </summary>
public class CWMove : MonoBehaviour
{
    
    
    /// <summary>
    /// 根据轿厢状态判断对重运行状态
    /// </summary>
    /// <param name="car">轿厢</param>
    public void CwMoveByCar(bool IsRun,Move carmove)
    {
    
    
        //轿厢是否在移动
        if (IsRun == true)
        {
    
    
            //轿厢往上还是往下移动
            if (carmove.carCurrentState == Move.CarState.down)
            {
    
    
                this.transform.Translate(0,0, -carmove.MoveSpeed*3 * Time.deltaTime);
            }
            else if (carmove.carCurrentState == Move.CarState.up)
                this.transform.Translate(0, 0,carmove.MoveSpeed *3* Time.deltaTime);
        }
        else
            this.transform.Translate(0, 0, 0);
    }
}


3、开关门动画

1>挂载到物体上的动画类

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 开关门动画,定义需要播放的动画片段名称
/// </summary>
public class DoorAnimation : MonoBehaviour
{
    
    

    public string[] BuildingDoor; 

    /// <summary>
    /// 行为类
    /// </summary>
    public Animations action;
    private void Awake()
    {
    
    
        //查找后代中具有Animation组件的子物体(有则返回第一个,无返回null)
        action = new Animations(GetComponentInChildren<Animation>());
    }
}

2>普通C#动画类

  • 目前遇到问题----如何将同一个动画正播后停止在最后一帧,在过几秒钟后将该动画倒播回去
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 动画行为类,提供有关动画的行为
/// </summary>
public class Animations 
{
    
    
    //附加在敌人模型上的动画组件引用
    private Animation anim;

    /// <summary>
    /// 创建动画行为类
    /// </summary>
    /// <param name="anim">附加在敌人模型上的动画组件引用</param>
    public Animations(Animation anim)
    {
    
    
        this.anim = anim;
    }

    //播放动画
    public void Play(string animName)
    {
    
    
        //anim.CrossFade(animName);
        anim.Play(animName);
    }

    /// <summary>
    /// 判断指定动画是否正在播放
    /// </summary>
    /// <param name="animName">动画片段名称</param>
    /// <returns></returns>
    public bool IsPlaying(string animName)
    {
    
    
        return anim.IsPlaying(animName);
    }
}

4、轿厢AI

  • 定义电梯状态信息----运动、停止、待机、检修
  • 根据每个状态来定义各自实现的功能
  • 运行过程如果到达目标层----状态改为停止
  • 若不在规定的楼层停止----状态改为检修,在规定的楼层停止----播放开关门动画,没人后状态改为待机
  • 若在待机过程中,目标位置发生改变----运行状态改为运动
  • 欠缺:停止过程的动画只能播放一次(目前是倒播),DeBug过程他也能进去,但实际就是不管用,试了好几个函数都没用(气的暂停这步了)
  • 欠缺:检修状态的事件暂时没想到
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 轿厢AI
/// </summary>
[RequireComponent(typeof(DoorAnimation))]
[RequireComponent(typeof(StatusInfo))]


public class CarAI : MonoBehaviour
{
    
    
    //定义轿厢状态的枚举类型
    public enum State
    {
    
    
        Run,//运行
        Stop,//停止
        Check,//故障
        Rest//休息
    }

    private State currentState = State.Run;
    private DoorAnimation anim;
    private Move move;
    private CWMove cw;
    private FloorInfo floor;
    private StatusInfo stf;
    
    private void Start()
    {
    
    
        anim = GetComponent<DoorAnimation>();
        move = GetComponentInChildren<Move>();
        cw = GetComponentInChildren<CWMove>();
        floor = GetComponentInChildren<FloorInfo>();
        floor.AddFloor();
        stf = GetComponent<StatusInfo>();
    }

    private void Update()
    {
    
    
        //判断
        switch(currentState)
        {
    
    
            case State.Run:
                Run();
                break;
            case State.Stop:
                Stop();
                break;
            case State.Rest:
                Rest();
                break;
            case State.Check:
                Check();
                break;
        }
    }

    /// <summary>
    /// 判断在电梯停在哪层
    /// </summary>
    private void Stop()
    {
    
    
        //判断电梯是否停好了
        bool IsStopOk = false;

        int fl = 0;
        //运行停止
        //播放轿厢开门动画
        foreach (KeyValuePair<int,Vector3>kv in floor.Floor)
        {
    
    
            //判断当前位置与某一楼层的位置是否接近
            if((decimal)Vector3.Distance(move.transform.position, kv.Value) < 0.05m)
            {
    
    
                if (kv.Key == -1)
                {
    
    
                    anim.action.Play(anim.BuildingDoor[0]);
                }                    
                else
                {
    
    
                    anim.action.Play(anim.BuildingDoor[kv.Key]);//播放开门动画
                    fl = kv.Key;//拿到楼层信息
                }
                                    
                IsStopOk = true;//电梯停好了
                break;
            }
        }
        
        
        if(IsStopOk==true)
        {
    
    
            float i = 0;
            while (true)
            {
    
       
                i += Time.deltaTime;
                //如果超过2秒没人,播放关门动画
                if (i >= 2 && stf.MovePeople() == false)
                {
    
    
                    anim.action.Close(anim.BuildingDoor[fl]);
                    currentState = State.Rest;
                    break;
                }
            }                      
        }
        else
        {
    
    
            currentState = State.Check;//将电梯标记为出故障
        }
    }

    private void Run()
    {
    
    
        //执行寻路(调用move中的寻路方法)
        move.Pathfinding();
        cw.CwMoveByCar(move.Pathfinding(), move);
        //如果寻路结束,修改状态为停止
        if (move.Pathfinding() == false) currentState = State.Stop;
    }


    private void Rest()
    {
    
    
        //Debug.Log("电梯处于待机状态");
        move.carCurrentState = Move.CarState.stop;
        move.CarMovement();
        if (move.CarStateJudge(move.points[0].position) != Move.CarState.stop)
            currentState = State.Run;

    }

    private void Check()
    {
    
    
        //Debug.Log("电梯故障,请及时处理");
    }
}


三、效果图

在这里插入图片描述

  • 整体缺陷:
    1、使用Translate进行移动,会存在到了指定楼层后一直上下抖动的情况,导致动画不播放(其实把判断的距离设大就行,但是看起来就不贴合实际了)
    2、同一个动画的正播和反播问题
    3、初步想法,后期有待完善

猜你喜欢

转载自blog.csdn.net/Lcl_huolitianji/article/details/121803014