3d_unity_hw4

本文介绍了在Unity3D中如何实现3D游戏的基本操作,包括构建天空盒、游戏对象的分类及其使用。此外,详细阐述了游戏编程实践,如动作分离,设计了裁判类来判断游戏结束条件,以及场景控制器如何协调游戏对象的动作。通过动作管理类统一管理角色和船只的移动,实现了更加流畅的游戏交互。最后,展示了游戏的裁判逻辑和场景控制器的关键代码,以确保游戏流程的正确性。
摘要由CSDN通过智能技术生成

3D游戏编程与设计_hw4

1. 基本操作演练

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

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

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

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

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();
			}
		}
    }

成果展示

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值