Unity3d 实现简单的剧情系统

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_18192161/article/details/79229109

剧情中需要做什么?
1).创建物体
2).基础位移,旋转
3).UI控制
4).语音控制
等等…

命令类:


/// <summary>
/// 剧情命令基类
/// </summary>
public abstract class PlotCommand
{
    /// <summary>
    /// 剧情数据
    /// </summary>
    protected PlotInfo PlotInfo { get; set; }
    /// <summary>
    /// 上一条命令
    /// </summary>
    public PlotCommand PreCommand { get; set; }
    /// <summary>
    /// 下一条命令
    /// </summary>
    public PlotCommand NextCommand { get; set; }
    /// <summary>
    /// 解析
    /// </summary>
    /// <param name="plotInfo"></param>
    /// <param name="command"></param>
    /// <returns></returns>
    public bool OnParse(PlotInfo plotInfo, string command)
    {
        PlotInfo = plotInfo;
        return Parse(command);
    }
    /// <summary>
    /// 解析
    /// </summary>
    /// <param name="command"></param>
    /// <returns></returns>
    public abstract bool Parse(string command);
    /// <summary>
    /// 执行
    /// </summary>
    /// <returns></returns>
    public abstract bool DoAction();
    /// <summary>
    /// 命令结束
    /// </summary>
    protected void OnComandFinsh()
    {
        if (NextCommand != null)
            NextCommand.DoAction();
        else
            PlotInfo.Close();
    }

}

例如:

/// <summary>
/// 创建角色
/// </summary>
public class CreateActor : PlotCommand
{
    public string ActorName;
    public string ModelName;

    public override bool DoAction()
    {
        Debug.Log(string.Format("创建角色:{0}-{1}",ActorName,ModelName));
        PlotInfo.PlotManager.CreateActor(ActorName, ModelName);
        OnComandFinsh();
        return true;
    }

    public override bool Parse(string command)
    {
        string[] param = command.Split(',');
        ActorName = param[0];
        ModelName = param[1];
        return true;
    }
}

/// <summary>
/// 设置坐标
/// </summary>
public class SetPosition : PlotCommand
{
    public string ActorName;
    public Vector3 ActorPos;
    public float Time;

    public int type;

    public override bool DoAction()
    {
        Debug.Log(string.Format("设置角色坐标:{0}-{1}", ActorName, ActorPos));
        switch (type)
        {
            case 2:
                PlotInfo.PlotManager.SetActorPos(ActorName, ActorPos);
                break;
            case 3:
                PlotInfo.PlotManager.SetActorPos(ActorName, ActorPos,Time);
                break;
        }

        OnComandFinsh();
        return false;
    }

    public override bool Parse(string command)
    {
        string[] param = command.Split(',');
        ActorName = param[0];
        ActorPos = PlotTools.ParseVector3(param[1]);
        type = 2;
        if (param.Length > 2)
        {
            Time = float.Parse(param[2]);
            type = 3;
        }
        return true;
    }
}
/// <summary>
/// 设置旋转
/// </summary>
public class SetRotation : PlotCommand
{
    public string ActorName;
    public Vector3 Angle;
    public float Speed;
    public override bool DoAction()
    {
        Debug.Log(string.Format("设置角色转向:{0}-{1}", ActorName, Angle));
        PlotInfo.PlotManager.SetActorRot(ActorName,Angle, Speed);
        OnComandFinsh();
        return true;
    }

    public override bool Parse(string command)
    {
        string[] param = command.Split(',');
        ActorName = param[0];
        string[] rot = param[1].Split('|');
        Angle = PlotTools.ParseVector3(param[1]);
        Speed = float.Parse(param[2]);
        return true;
    }
}

等等,可以进行其他命令扩展,比如UI,音效,以及延时命令等

//延时
public class Delay : PlotCommand
{
    public float DelayTime;
    public override bool DoAction()
    {
        Debug.Log(string.Format("延时:{0}", DelayTime));
        PlotTools.Instance.Delay(DelayTime, delegate {
            OnComandFinsh();
        });
        return false;
    }

    public override bool Parse(string command)
    {
        DelayTime = float.Parse(command);
        return true;
    }
}

创建一个工程用来生成命令


public class PlotFactory {

    private static Hashtable lookUpType = new Hashtable();
    public static PlotCommand Create(string name)
    {
        PlotCommand c = null;
        try
        {
            var type = (Type)lookUpType[name];
            lock (lookUpType)
            {
                if (type == null)
                {
                    //Assembly curAssembly = Assembly.GetEntryAssembly();
                    type = Type.GetType(name);
                    //type = Type.GetType();
                    lookUpType[name] = type;
                }
            }
            if (type != null)
                c = Activator.CreateInstance(type) as PlotCommand;
        }
        catch (Exception ex)
        {
            Debug.Log( string.Format("创建剧情命令{0},失败!!",ex.Message));
        }
        return c;
    }
}

剧情数据类:

using System.Collections.Generic;

public class PlotInfo
{
    private List<PlotCommand> PlotCommands = new List<PlotCommand>();

    public PlotInfo() { }

    public PlotManager PlotManager { get; private set; }

    public PlotInfo(PlotManager plotManager)
    {
        PlotManager = plotManager;
    }

    /// <summary>
    /// 开始剧情
    /// </summary>
    public void Play()
    {
        PlotManager.ClearPlotActor();
        if (Count > 0)
            PlotCommands[0].DoAction();
    }

    /// <summary>
    /// 停止剧情
    /// </summary>
    public void Stop()
    {

    }

    /// <summary>
    /// 关闭剧情
    /// </summary>
    public void Close()
    {

    }

    public void AddCommand(PlotCommand PlotCommand)
    {
        if (Count > 0)
        {
            PlotCommand.PreCommand = PlotCommands[Count - 1];
            PlotCommands[Count - 1].NextCommand = PlotCommand;
        }
        PlotCommands.Add(PlotCommand);
    }

    public int Count
    {
        get{ return PlotCommands.Count;}
    }

}

写了一个实现接口

public interface IPlot {

    //创建角色
    void CreateActor(string name, string assetName);

    //设置坐标
    void SetActorPos(string name, Vector3 pos);
    void SetActorPos(string name, Vector3 pos, float time);
    void SetActorPos(int index, Vector3 pos);
    void SetActorPos(int index, Vector3 pos, float time);


    //设置旋转
    void SetActorRot(string name, Vector3 Angle, float time);
    void SetActorRot(int index, Vector3 Angle, float time);

    void Play(string plotName);

}

管理类:

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

[RequireComponent(typeof(PlotTools))]
public class PlotManager : MonoBehaviour, IPlot
{
    private static PlotManager _instance;
    public static PlotManager Instance
    {
        get
        {
            return _instance;
        }
    }

    /// <summary>
    /// 解析剧情
    /// </summary>
    /// <param name="command"></param>
    /// <returns></returns>
    public static PlotInfo ParsePlot(string command)
    {
        command = command.Replace("\r", "");
        string[] commandAry = command.Split('\n');
        PlotInfo pi = new PlotInfo(PlotManager.Instance);
        for (int i = 0; i < commandAry.Length; i++)
        {
            if (commandAry[i].Equals("")) continue;
            string[] commandStruct = commandAry[i].Split(':');
            PlotCommand pc = PlotFactory.Create(commandStruct[0]);
            if (pc != null)
            {
                pc.OnParse(pi,commandStruct[1]);
                pi.AddCommand(pc);
            }
            else
                Debug.Log(string.Format("创建剧情命令{0},失败!!", commandStruct[0]));
        }
        return pi;
    }

    /// <summary>
    /// 解析剧情
    /// </summary>
    /// <param name="PlotId"></param>
    /// <returns></returns>
    public static PlotInfo ParsePlot(int PlotId)
    {
        return null;
    }


    public GameObject obj;
    public TextAsset plot;

    private Dictionary<string, GameObject> PlotActors = new Dictionary<string, GameObject>();

    void Awake()
    {
        _instance = this;
    }

    void Start()
    {
        PlotInfo pi = ParsePlot(plot.text);
        pi.Play();
    }

    /// <summary>
    /// 创建角色
    /// </summary>
    /// <param name="name"></param>
    /// <param name="assetName"></param>
    public void CreateActor(string name, string assetName)
    {
        GameObject actor = GameObject.Instantiate(obj) as GameObject;
        actor.transform.parent = this.transform;
        actor.name = name;
        PlotActors.Add(actor.name,actor);
    }

    /// <summary>
    /// 设置角色位置
    /// </summary>
    /// <param name="name"></param>
    /// <param name="pos"></param>
    public void SetActorPos(string name, Vector3 pos)
    {
        GameObject actor = GetActor(name);
        if (actor != null)
            actor.transform.localPosition = pos;
    }

    /// <summary>
    /// 设置角色位置 时间
    /// </summary>
    /// <param name="name"></param>
    /// <param name="pos"></param>
    /// <param name="time"></param>
    public void SetActorPos(string name, Vector3 pos, float time)
    {
        MoveTick tick = new MoveTick();
        tick.moveActor = GetActor(name).transform;
        tick.moveEndPos = pos;
        float dis = Vector3.Distance(tick.moveActor.localPosition, pos);
        tick.moveSpeed = dis / time;
        PlotTools.Instance.AddPlotTick(tick);
    }

    /// <summary>
    /// 设置角色选择 速度
    /// </summary>
    /// <param name="name"></param>
    /// <param name="Angle"></param>
    /// <param name="speed"></param>
    public void SetActorRot(string name, Vector3 Angle, float speed)
    {
        RotTick tick = new RotTick();
        tick.rotActor = GetActor(name).transform;
        tick.EndAngle = Angle;
        tick.rotSpeed = speed;
        PlotTools.Instance.AddPlotTick(tick);
    }

    /// <summary>
    /// 获取角色
    /// </summary>
    /// <param name="name"></param>
    /// <returns></returns>
    public GameObject GetActor(string name)
    {
        if (PlotActors.ContainsKey(name))
            return PlotActors[name];
        return null;
    }

    /// <summary>
    /// 获取角色
    /// </summary>
    /// <param name="index"></param>
    /// <returns></returns>
    public GameObject GetActor(int index)
    {
        //-1 表示为主角
        if (index == -1)
        {
            //获取主角数据
            return null;
        }
        if (PlotActors.Count > index)
        {
            List<GameObject> actor = new List<GameObject>(PlotActors.Values);
            return actor[index];
        }
        return null;
    }

    /// <summary>
    /// 清楚角色
    /// </summary>
    public void ClearPlotActor()
    {
        List<GameObject> obj = new List<GameObject>(PlotActors.Values);
        for (int i = 0; i < obj.Count; i++)
        {
            Destroy(obj[i]);
        }
        PlotActors.Clear();
    }

    public void SetActorPos(int index, Vector3 pos)
    {
        throw new NotImplementedException();
    }

    public void SetActorPos(int index, Vector3 pos, float time)
    {
        throw new NotImplementedException();
    }

    public void SetActorRot(int index, Vector3 Angle, float time)
    {
        throw new NotImplementedException();
    }

    public void Play(string plotName)
    {
        throw new NotImplementedException();
    }
}


循环方法,例如位移,旋转等需要没帧执行的

public abstract class PlotTick
{
    public System.Action OnFinsh;
    public bool IsFinsh { get; protected set; }
    public abstract void OnUpdate();
}

public class MoveTick : PlotTick
{
    public float moveSpeed;
    public Vector3 moveEndPos;
    public Transform moveActor;

    public override void OnUpdate()
    {
        moveActor.localPosition = Vector3.MoveTowards(moveActor.localPosition, moveEndPos, moveSpeed * Time.deltaTime);
        if (moveActor.localPosition == moveEndPos)
        {
            IsFinsh = true;
            if (OnFinsh != null)
                OnFinsh();
        }
    }
}

public class RotTick : PlotTick
{
    public float rotSpeed;
    public Vector3 EndAngle;
    public Transform rotActor;

    public override void OnUpdate()
    {
        rotActor.localEulerAngles = Vector3.Lerp(rotActor.localEulerAngles, EndAngle, rotSpeed * Time.deltaTime);
        if (Vector3.Distance(rotActor.localEulerAngles,EndAngle)<0.02f)
        {
            IsFinsh = true;
            if (OnFinsh != null)
                OnFinsh();
        }
    }
}

工具类:

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

public class PlotTools : MonoBehaviour {

    private static PlotTools _instance;
    public static PlotTools Instance
    {
        get {
            return _instance;
        }
    }

    void Awake()
    {
        _instance = this;
    }

    void Start()
    {

    }

    private List<PlotTick> plotTick = new List<PlotTick>();
    private List<PlotTick> removePlotTick = new List<PlotTick>();

    void Update()
    {
        removePlotTick.Clear();
        for (int i = 0; i < plotTick.Count; i++)
        {
            if (!plotTick[i].IsFinsh)
            {
                plotTick[i].OnUpdate();
                continue;
            }
            removePlotTick.Add(plotTick[i]);
        }
        for (int i = 0; i < removePlotTick.Count; i++)
        {
            RemovePlotTick(removePlotTick[i]);
        }
    }

    public void AddPlotTick(PlotTick tick)
    {
        plotTick.Add(tick);
    }

    public void RemovePlotTick(PlotTick tick)
    {
        if(plotTick.Contains(tick))
            plotTick.Remove(tick);
    }

    /// <summary>
    /// 延时执行某个方法
    /// </summary>
    /// <param name="time"></param>
    /// <param name="OnFinsh"></param>
    public void Delay(float time, System.Action OnFinsh)
    {
        StartCoroutine(IeDelay(time, OnFinsh));
    }

    IEnumerator IeDelay(float time, System.Action OnFinsh)
    {
        yield return new WaitForSeconds(time);
        if (OnFinsh != null)
            OnFinsh();
    }

    public static Vector3 ParseVector3(string content)
    {
        string[] data = content.Split('|');
        return new Vector3(float.Parse(data[0]), float.Parse(data[1]), float.Parse(data[2]));
    }

    public static Vector2 ParseVector2(string content)
    {
        string[] data = content.Split('|');
        return new Vector2(float.Parse(data[0]), float.Parse(data[1]));
    }

}

配置剧情:

这里写图片描述

最后需要将PlotManager和PlotTools脚本挂载到物体上
这里写图片描述

播放剧情,就是PlotManager中的Start方法

   void Start()
    {
        PlotInfo pi = ParsePlot(plot.text);
        pi.Play();
    }

为了演示,我将创建物体,剧情读取都直接进行了面板拖拽的方式,在正式的项目中需要调用项目的中创建接口和资源接口。

后期扩展可以写个时间轴工具,添加事件等等。
最终实现创建、位移,旋转等操作

这里写图片描述

猜你喜欢

转载自blog.csdn.net/qq_18192161/article/details/79229109
今日推荐