ch04游戏对象与图形基础


一、加载Fantasy Skybox FREE,构建自己的游戏场景

  • 这是我10分钟构建游戏场景的视频,视频五倍速链接: Unity制作游戏场景——5倍速
  • 主要是运用老师课件上讲的知识,包括Terrain内工具的使用
    在这里插入图片描述
  • 以及一些Unity基础知识,熟练掌握快捷键会更好

二、写一个简单的总结,总结游戏对象的使用

  1. 游戏对象:游戏中的一切东西都可以是游戏对象,游戏对象初始化带有必要的Tranformer组件,用来定位

  2. 创建GameObject

    • 通过编辑器GameObject可视化添加:可以直接拖拽到场景中或者添加在左侧栏
    • 脚本添加:Object.Instantiate()
  3. 获取GameObject

    • 在脚本中定义public变量,可以直接拖拽
    • 通过对象名字Find函数
    • 其他方法:(由于没有测试过,谨慎使用)
      • 通过标签获取单个游戏对象(FindWithTag方法)
      • 通过标签获取多个游戏对象(FindGameObjectsWithTags方法)
      • 通过类型获取单个游戏对象(FindObjectOfType方法)
      • 通过类型获取多个游戏对象(FindObjectsOfType方法)
  4. 为GameObject添加或者删除组件

    • 在Inspector中点击右上角的三个点可以手动添加删除
    • AddComponent<>()或者GetComponent<>()
  5. 发送广播和信息(查询资料得知:Message相关有3条指令)

    • SendMessage (“函数名”,参数,SendMessageOptions) //GameObject自身的Script
    • BroadcastMessage (“函数名”,参数,SendMessageOptions) //自身和子Object的Script
    • SendMessageUpwards (“函数名”,参数,SendMessageOptions) //自身和父Object的Script
  6. 克隆和预设

    • 将你设计好的GameObject拖到底栏(最好是perfabs文件夹中)可以得到你自己的预设
    • 将预设和其他GameObject一样拖出使用
    • 脚本:
      • 这里的"Perfabs/Ocean"是预设存储路径
      • typeof(GameObject))是类型
      • ocean_pos是预先定义好放置预设的位置
      • Quaternion.identity是预设的角度
 Ocean = Object.Instantiate(Resources.Load("Perfabs/Ocean", typeof(GameObject)), ocean_pos, Quaternion.identity, null) as GameObject;
  1. 运动、旋转和缩放

这些都可以通过快捷栏的工具可视化拖拽

  • 运动
    • 可视化直接修改物体的position
    • transform.Translate(Vector3)
    • transform.position += Vector3
  • 旋转
    • 自身:transform.Rotate()
    • 公转:transform.RotateAround()
  • 缩放:transform.localScale = Vector(x, y, z)
  1. 销毁GameObject
  • GameObject.Destroy
  • Destroy(gameObject);
  1. 更多操作参考: Unity3D官方中文手册

三、魔鬼与牧师 动作分离版

依据老师上课讲的框架,实现了魔鬼与牧师动作分离版

在这里插入图片描述

1. 解决方案

在这里插入图片描述


游戏运行时只需要将GameController.cs挂载在摄像机上就可以啦


2. 主要更新

  • InterfaceApplication.cs: 将所有接口类独立出来放在一起
    • SceneController
    • UserAction
    • ISSActionCallback
  • SSActionController.cs: 分离动作
    • SSAction
    • SSMoveToAction
    • SequenceAction
    • SSActionManager:动作管理基类
    • ActionManager:动作管理类
      • moveBoat:控制船的移动
      • moveRole:控制角色的移动

SSActionController.cs


这里参考了师兄师姐的代码

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

using interfaceApplication;


public class SSAction : ScriptableObject
{
    public bool enable = true;//是否存在
    public bool destory = false;//是否删除

    public GameObject gameobject;//动作对象
    public Transform transform;//动作对象的transformer
    public ISSActionCallback callback;//回调函数


    protected SSAction() { }

    public virtual void Start()
    {
        //是指在无法实现请求的方法或操作时引发的异常。
        throw new System.NotImplementedException();
    }

    public virtual void Update()
    {
        throw new System.NotImplementedException();
    }

}



public class SSMoveToAction : SSAction
{

    public Vector3 target; //desitination
    public float speed; 

    private SSMoveToAction() { }

    public static SSMoveToAction GetSSAction(Vector3 _target, float _speed)
    {
        //创建一个方法 方法中传入一个类的名称 然后就能返回一个这个类的实例
        SSMoveToAction action = ScriptableObject.CreateInstance<SSMoveToAction>();
        action.target = _target;
        action.speed = _speed;
        return action;
    }

    public override void Start()
    {
    }

    public override void Update()
    {
        //这里只是把该perfas从当前位置移动到目标位置
        this.transform.position = Vector3.MoveTowards(this.transform.position, target, speed * Time.deltaTime);
        //完成
        if (this.transform.position == target)
        {
            this.destory = true;
            this.callback.SSActionEvent(this);
        }
    }

}



//动作组合,比如说角色的移动要先平移到岸边,再平移到船上或者岸上
public class SequenceAction : SSAction, ISSActionCallback
{
    public List<SSAction> sequence; //动作列表
    public int repeat = -1; //-1就是无限循环做组合中的动作
    public int start = 0; //当前做的动作的索引

    public static SequenceAction GetSSAcition(int repeat, int start, List<SSAction> sequence)
    {
        
        SequenceAction action = ScriptableObject.CreateInstance<SequenceAction>();
        action.sequence = sequence;
        action.repeat = repeat;
        action.start = start;
        return action;
    }

    public override void Start()
    {
        //执行每个动作
        foreach (SSAction action in sequence)
        {
            action.gameobject = this.gameobject;
            action.transform = this.transform;
            action.callback = this; //每一个动作的回调函数为该动作组合
            action.Start();
        }
    }

    public override void Update()
    {
        if (sequence.Count == 0) return;
        if (start < sequence.Count)
        {
            sequence[start].Update(); //start在回调函数中递加
        }
    }

    //实现接口
    public void SSActionEvent(
        SSAction source, SSActionEventType events = SSActionEventType.competeted,
        int intParam = 0, string strParam = null, Object objectParam = null
    )
    {
        source.destory = false; 
        this.start++; //下一个动作
        if (this.start >= sequence.Count)
        {
            this.start = 0;
            if (repeat > 0) repeat--;
            if (repeat == 0)
            { //动作组合结束
                this.destory = true;
                this.callback.SSActionEvent(this);
            }
        }
    }
}





//动作管理基类
public class SSActionManager : MonoBehaviour, ISSActionCallback {
    private Dictionary<int, SSAction> actions = new Dictionary<int, SSAction>(); //动作字典
    private List<SSAction> waitingAdd = new List<SSAction>(); //等待执行
    private List<int> waitingDelete = new List<int>(); //等待删除             

    protected void Update() {
        foreach (SSAction ac in waitingAdd) {
            actions[ac.GetInstanceID()] = ac;                                       
        }
        waitingAdd.Clear();

        //对于字典中每一个pair,看是执行还是删除
        foreach (KeyValuePair<int, SSAction> kv in actions) {
            SSAction ac = kv.Value;
            if (ac.destory) {
                waitingDelete.Add(ac.GetInstanceID());
            }else if (ac.enable) {
                ac.Update();
            }
        }

        //删除所有已完成的动作并清空待删除列表
        foreach (int key in waitingDelete) {
            SSAction ac = actions[key];
            actions.Remove(key);
            Object.Destroy(ac);
        }
        waitingDelete.Clear();
    }

    public void RunAction(GameObject gameobject, SSAction action, ISSActionCallback manager) {
        action.gameobject = gameobject;
        action.transform = gameobject.transform;
        action.callback = manager;
        waitingAdd.Add(action);
        action.Start();
    }

    public void SSActionEvent(
        SSAction source, SSActionEventType events = SSActionEventType.competeted,
        int intParam = 0, string strParam = null, Object objectParam = null) {

    }
}

//动作管理类
public class ActionManager : SSActionManager {
    public GameController sceneController;
    private SequenceAction boatMove;
    private SequenceAction roleMove;

    protected void Start() {
        sceneController = (GameController)Director.getInstance().currentSceneController;
        sceneController.actionManager = this;
    }

    protected new void Update() {
        base.Update();
    }

    //移动船的动作
    public void moveBoat(GameObject boat, Vector3 endPos, float speed) {
        SSAction action1 = SSMoveToAction.GetSSAction(endPos, speed);
        boatMove = SequenceAction.GetSSAcition(0, 0, new List<SSAction> { action1 });
        this.RunAction(boat, boatMove, this);
    }


    //移动角色
    public void moveRole(GameObject role, Vector3 middlePos, Vector3 endPos, float speed) {
        //两段移动
        SSAction action1 = SSMoveToAction.GetSSAction(middlePos, speed);
        SSAction action2 = SSMoveToAction.GetSSAction(endPos, speed);
        //两个动作
        roleMove = SequenceAction.GetSSAcition(1, 0, new List<SSAction> { action1, action2 });
        this.RunAction(role, roleMove, this);
    }

}


动作管理的使用


  1. 定义接口
    在这里插入图片描述

  2. 在GameController.cs中使用actionManager来管理动作

public void PAndVIsClicked(PAndVController POrVCtrl)
    {

        Vector3 middlePos,endPos;

        //下船上岸
        if (POrVCtrl.isOnBoat())
        {
            //先判断现在是哪一个岸
            CoastController whichCoast;
            if (boat.get_which_coast() == 1)
            {
                whichCoast = rightCoast;
               
            }
            else
            {
                whichCoast = leftCoast;
            }
            //目的地
            endPos = whichCoast.getAvailablePosition();
            //中间位置
            middlePos = new Vector3(POrVCtrl.getGameObject().transform.position.x,endPos.y, endPos.z);


            //动作
            boat.GetOffBoat(POrVCtrl.getName());
		
            /*动作管理器管理*/
            actionManager.moveRole(POrVCtrl.getGameObject(),middlePos,endPos,boat.boatSpeed);

            //其他操作
            //POrVCtrl.setPosition(whichCoast.getAvailablePosition());
            POrVCtrl.getOnCoast(whichCoast);
            whichCoast.getOnCoast(POrVCtrl);
            

        }
        else//下岸上船
        {
            CoastController whichCoast = POrVCtrl.getCoastController();

            if (boat.getEmptyIndex() == -1)
            {   //船满了
                return;
            }
            else if(whichCoast.get_which_coast() != boat.get_which_coast())
            {  
                //船没靠岸
                return;
            }
            else
            {

                //目的地
                endPos = boat.getAvailablePosition();
                //中间位置
                middlePos = new Vector3( endPos.x, POrVCtrl.getGameObject().transform.position.y, endPos.z);
                //动作
                whichCoast.getOffCoast(POrVCtrl.getName());
                
                 /*动作管理器管理*/
                actionManager.moveRole(POrVCtrl.getGameObject(),middlePos,endPos ,POrVCtrl.speed);
                
                //其他操作
                //POrVCtrl.setPosition(endPos);
                POrVCtrl.getOnBoat(boat);
                boat.GetOnBoat(POrVCtrl);

            } 
        }
        //检查游戏状态
        userGUI.status = judge.endGame();
    }



    //移动船的组件
    public void moveBoat()
    {

        
        if (boat.isEmpty())
            return;

        //boat.Move();
        //动作管理器管理
        actionManager.moveBoat(boat.getGameobject(), boat.getToPosition(), boat.boatSpeed);
        boat.changeCoast();

        //检查游戏状态
        userGUI.status = judge.endGame();
    }
  • 本次代码还更新了水的波浪船的摇晃,因为这个是伴随整个游戏的,所以没有使用动作管理,而是直接在GameObject更新

在GameController.cs中Update


    void Update()
    {
        //这两个状态整个游戏都有,不需要动作类的管理
        Ocean.WaterWave();
        boat.Shake();

    }

波浪


    public enum Superposition
    {
        //周期叠加
        Cycle,
        //振幅叠加
        Amplitude
    }

    public class WaterController
    {
        //水的位置
        readonly Vector3 ocean_pos = new Vector3(0, .4F, 0);

        //Ocean
        GameObject Ocean;

        //水波振幅
        public float amplitude = .7f;

        //波动的速度
        public float speed = 2.8f;

        //振幅叠加方式
        public Superposition superpositon = Superposition.Amplitude;

        //模型网格
        private Mesh sharedMesh;

        //网格初始顶点坐标数组
        private Vector3[] baseVertex;

        //网格修改后的顶点坐标数组(也就是每一次update之后)
        private Vector3[] nowVertex;


        public WaterController(string _name) 
        {   
            Ocean = Object.Instantiate(Resources.Load("Perfabs/Ocean", typeof(GameObject)), ocean_pos, Quaternion.identity, null) as GameObject;
            Ocean.name = "ocean";
            //初始化
            sharedMesh = Ocean.GetComponent<MeshFilter>().sharedMesh;
            baseVertex = sharedMesh.vertices;
            nowVertex = sharedMesh.vertices;
        }

        //GameObject的Update调用
        public void WaterWave()
        {
            //更新波浪顶点
            for (int i = 0; i < baseVertex.Length; i++)
            {
                nowVertex[i] = baseVertex[i];
                switch (superpositon)
                {
                    case Superposition.Cycle:
                        nowVertex[i].y += Mathf.Sin(Time.time * speed + baseVertex[i].x + baseVertex[i].z) * amplitude;
                        break;
                    case Superposition.Amplitude:
                        nowVertex[i].y += Mathf.Sin(Time.time * speed + baseVertex[i].x) * amplitude + Mathf.Sin(Time.time * speed + baseVertex[i].z) * amplitude;
                        break;


                }
            }

            sharedMesh.vertices = nowVertex;
        }

    }
    

船的摇晃



//绕z轴摇摆的速度
float z_Speed = 3f;
//绕x轴摇摆的速度
float x_Speed = 3f;
//绕y轴摇摆的速度
float y_Speed = 3f;

public void Shake()
        {
            //MoveComponent.setAngle();
            // 绕Z轴摇晃  
            if (boat.transform.eulerAngles.z >= 3 && boat.transform.eulerAngles.z <= 180)
            {
                z_Speed = -z_Speed;
            }
            else if (boat.transform.eulerAngles.z <= (360 - 3) && boat.transform.eulerAngles.z >= 180)
            {
                z_Speed = -z_Speed;
            }
            // 绕X轴摇晃  
            if (boat.transform.eulerAngles.x >= 3 && boat.transform.eulerAngles.x <= 180)
            {
                x_Speed = -x_Speed;
            }
            else if (boat.transform.eulerAngles.x >= 180 && boat.transform.eulerAngles.x <= (360 - 3))
            {
                x_Speed = -x_Speed;
            }
            boat.transform.Rotate(z_Speed * Time.deltaTime, 0, z_Speed * Time.deltaTime);

        }

3. 链接


实现效果参考:Unity3D-牧师与魔鬼(升级版)


项目地址:项目地址


猜你喜欢

转载自blog.csdn.net/yesor_not/article/details/127624132