欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

3d_unity_hw4

程序员文章站 2024-03-18 17:06:16
...

3D游戏编程与设计_hw4

1. 基本操作演练

  • 下载 Fantasy Skybox FREE, 构建自己的游戏场景

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

    首先创建一个Material,然后在shader选项选择Skybox/6 Sided,会出现六个插入Texture的框框,在Fantasy Skybox FREE中选择一个自己喜欢的图片放入,完成后挂载到Main Camera中,运行游戏。
    效果图:
    3d_unity_hw4

    游戏对象的使用总结:
    以我的理解,游戏对象可以分为辅助对象和实体对象。辅助对象如摄像头、光线和空游戏对象等,在游戏运行时看不到具体的对象,但却对游戏运行的效果和功能的实现起着不可或缺的作用:光线可以增加场景亮度;空游戏对象可以用来挂载不需要游戏对象作为载体的脚本。而实体对象则是游戏运行时可以看见甚至操作的具体对象。

2. 编程实践

牧师与魔鬼 动作分离版
【2019开始的新要求】:设计一个裁判类,当游戏达到结束条件时,通知场景控制器游戏结束

完整代码见我的Gitee

改进方案

所谓动作分离,即设置一个动作管理类来管理所有游戏对象的动作,而不用每个游戏对象调用自己的动作函数(即对游戏对象统一管理)。因此这次改进我们要设置一个管理类,同时管理船、牧师、恶魔的移动,而不为他们各自设计运动函数。

对象类

游戏的对象有:牧师、恶魔、船、岸、水。其中牧师和恶魔的行为类似因此有共同的父类RoleModel。每个Role和船创建时都为其添加一个Click类,使其在鼠标按下时可以进行对应动作。以RoleModel的构造函数为例:

    public RoleModel(string role_name)
    {
        if (role_name == "priest")
        {   Debug.Log("Priest created!");
            role = Object.Instantiate(Resources.Load("Priest", typeof(GameObject)), Vector3.zero, Quaternion.Euler(0, -90, 0)) as GameObject;
            role_sign = 0;
        }
        else
        {
            Debug.Log("Devil created!");
            role = Object.Instantiate(Resources.Load("Devil", typeof(GameObject)), Vector3.zero, Quaternion.Euler(0, -90, 0)) as GameObject;
            role_sign = 1;
        }
        click = role.AddComponent(typeof(Click)) as Click;
        Debug.Log("click created!");
        play_ani = role.AddComponent(typeof(PlayAnimation)) as PlayAnimation;
        click.SetRole(this);
    }

岸的对象设计关键在于Role站立的位置,还有其是出发岸还是结束岸。其构造代码如下:

    public LandModel(string land_mark)
    {
        positions = new Vector3[] {new Vector3(5,1,0), new Vector3(6,1,0), new Vector3(7,1,0),
            new Vector3(8,1,0), new Vector3(9,1,0), new Vector3(10,1,0)};
        if (land_mark == "start")
        {
            land = Object.Instantiate(Resources.Load("Land", typeof(GameObject)), new Vector3(8, -1.5f, 0f), Quaternion.identity) as GameObject;
            land_sign = 1;
        }
        else
        {
            land = Object.Instantiate(Resources.Load("Land", typeof(GameObject)), new Vector3(-8, -1.5f, 0f), Quaternion.identity) as GameObject;
            land_sign = -1;
        }
    }

Click类为玩家和游戏对象提供交互接口:

public class Click : MonoBehaviour
{
    IUserAction action;
    RoleModel role = null;
    BoatModel boat = null;
    public void SetRole(RoleModel role)
    {
        this.role = role;
    }
    public void SetBoat(BoatModel boat)
    {
        this.boat = boat;
    }
    void Start()
    {Debug.Log("Start!");
        action = SSDirector.GetInstance().CurrentScenceController as IUserAction;
    }
    void OnMouseDown()
    {   
        if (boat == null && role == null) return;
        if (boat != null)
            action.MoveBoat();
        else if(role != null)
            action.MoveRole(role);
    }
}

动作类

首先设计一个SSAction类作为所有动作的基类,其子类是具体动作的实现。SSAction包括动作的对象、是否正在进行动作等成员变量。具体声明如下:

public class SSAction : ScriptableObject            
{

    public bool enable = true;                      
    public bool destroy = false;                    

    public GameObject gameobject;                  
    public Transform transform;                    
    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;        //移动到的目的地
    public float speed;           //移动的速度

    private SSMoveToAction() { }
    public static SSMoveToAction GetSSAction(Vector3 target, float speed)
    {
        SSMoveToAction action = ScriptableObject.CreateInstance<SSMoveToAction>();//让unity自己创建一个MoveToAction实例,并自己回收
        action.target = target;
        action.speed = speed;
        return action;
    }

    public override void Update()
    {
        this.transform.position = Vector3.MoveTowards(this.transform.position, target, speed * Time.deltaTime);
        if (this.transform.position == target)
        {
            this.destroy = true;
            this.callback.SSActionEvent(this);      //告诉动作管理或动作组合这个动作已完成
        }
    }

    public override void Start()
    {
        //移动动作建立时候不做任何事情
    }
}

为了让游戏场景更真实,我们引入一个动作序列类SequenceAction,让角色上船和下船的动作分解为两步。SequenceAction继承了SSAction和ISSActionCallback(用于告知动作完成情况的类),代码如下:

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>();//让unity自己创建一个SequenceAction实例
        action.repeat = repeat;
        action.sequence = sequence;
        action.start = start;
        return action;
    }

    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.destroy = false;          //先保留这个动作,如果是无限循环动作组合之后还需要使用
        this.start++;
        if (this.start >= sequence.Count)
        {
            this.start = 0;
            if (repeat > 0) repeat--;
            if (repeat == 0)
            {
                this.destroy = true;               //整个组合动作就删除
                this.callback.SSActionEvent(this); //告诉组合动作的管理对象组合做完了
            }
        }
    }

    public override void Start()
    {
        foreach (SSAction action in sequence)
        {
            action.gameobject = this.gameobject;
            action.transform = this.transform;
            action.callback = this;                //组合动作的每个小的动作的回调是这个组合动作
            action.Start();
        }
    }

    void OnDestroy()
    {
        //如果组合动作做完第一个动作突然不要它继续做了,那么后面的具体的动作需要被释放
    }
}

总的动作管理类为SSActionManager,游戏开始时让场景控制器令自己为动作管理器,可调用的函数有移动船和移动角色。

public class MySceneActionManager : SSActionManager  //本游戏管理器
{

    private SSMoveToAction moveBoatToEndOrStart;     //移动船到结束岸,移动船到开始岸
    private SequenceAction moveRoleToLandorBoat;     //移动角色到陆地,移动角色到船上

    public Controllor sceneController;

    protected new void Start()
    {
        sceneController = (Controllor)SSDirector.GetInstance().CurrentScenceController;
        sceneController.actionManager = this;
    }
    public void moveBoat(GameObject boat, Vector3 target, float speed)
    {
        moveBoatToEndOrStart = SSMoveToAction.GetSSAction(target, speed);
        this.RunAction(boat, moveBoatToEndOrStart, this);
    }

    public void moveRole(GameObject role, Vector3 middle_pos, Vector3 end_pos, float speed)
    {   Debug.Log("moverole");
        SSAction action1 = SSMoveToAction.GetSSAction(middle_pos, speed);
        SSAction action2 = SSMoveToAction.GetSSAction(end_pos, speed);
        moveRoleToLandorBoat = SequenceAction.GetSSAcition(1, 0, new List<SSAction> { action1, action2 });
        this.RunAction(role, moveRoleToLandorBoat, this);
    }
}

裁判类

裁判类用于判断游戏是否结束,胜利还是失败。

public class Judge : MonoBehaviour
{
    public LandModel start_land;
    public LandModel end_land;
    public BoatModel boat;

    public Judge(LandModel _fromLand, LandModel _toLand, BoatModel _boat)
    {
        start_land = _fromLand;
        end_land = _toLand;
        boat = _boat;
    }

    public  int isOver()
    {
        int start_priest = (start_land.GetRoleNum())[0];
        int start_devil = (start_land.GetRoleNum())[1];
        int end_priest = (end_land.GetRoleNum())[0];
        int end_devil = (end_land.GetRoleNum())[1];

        if (end_priest + end_devil == 6)     //获胜
            return 2;

        int[] boat_role_num = boat.GetRoleNumber();
        if (boat.GetBoatSign() == 1)         //在开始岸和船上的角色
        {
            start_priest += boat_role_num[0];
            start_devil += boat_role_num[1];
        }
        else                                  //在结束岸和船上的角色
        {
            end_priest += boat_role_num[0];
            end_devil += boat_role_num[1];
        }
        if (start_priest > 0 && start_priest < start_devil) //失败
        {      
            return 1;
        }
        if (end_priest > 0 && end_priest < end_devil)        //失败
        {
            return 1;
        }
        return 0;                                             //未完成
    }
}

场景控制器类

场景控制器需要加载游戏对象、通知裁判类判断游戏胜负,并且每次需要游戏对象进行动作时,只要通知动作管理器执行相关动作即可。
其包含的成员变量:


    public LandModel start_land;            //开始陆地
    public LandModel end_land;              //结束陆地
    public BoatModel boat;                  //船
    private RoleModel[] roles;              //角色
    UserGUI user_gui;                       //胜负标志
    Judge judge;                            //裁判
	public MySceneActionManager actionManager;   //动作管理

以移动船为例,代码如下:

    public void MoveBoat()                  //移动船
    {
		if (boat.IsEmpty() || user_gui.sign != 0) return;
		actionManager.moveBoat(boat.getGameObject(),boat.BoatMoveToPosition(),boat.move_speed);   //动作分离版本改变
        user_gui.sign = judge.isOver();
		if (user_gui.sign == 1)
		{
			for (int i = 0; i < 3; i++)
			{
				roles[i].PlayGameOver();
				roles[i + 3].PlayGameOver();
			}
		}
    }

成果展示

3d_unity_hw4

相关标签: 游戏分类

推荐阅读