Unity 3D GAME第四次作业

一、基本操作演练

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

使用Asset Store 下载Fantasy Skybox Free ,在Unity 中 菜单->Window->Package Manager,下载资源包,导入到项目中。
效果


加入自己绘制的地形:

在这里插入图片描在这里插入图片描述
述

2. 游戏对象使用的简单总结:

GameObject 可以通过unity 自带的基础资源如Cube,Sphere等几何体创建;GameObject 有Transform,纹理材质等多个属性可以调整来呈现不同效果,能给GameObject添加Component,例如音频,脚本等,丰富游戏场景和对象动作等效果;GameObject的实例化能够在图形界面完成或者用脚本代码完成;

二、编程实践(牧师与魔鬼 动作分离版):

演示视频
https://www.bilibili.com/video/BV18G4y1Z7Bz/

牧师与魔鬼 动作分离版

【2019新要求】:设计一个裁判类,当游戏达到结束条件时,通知场景控制器游戏结束

这次作业的就是在上次的基础上进行改进,主要改进的思路就是:设计一个抽象类作为游戏动作的基类;设计一个动作管理器类管理一组游戏动作的实现类;通过回调,实现动作完成时的通知。
我们先来看UML图(盗用的)

在这里插入图片描述

解释一下创建的几个类:

动作基类:public class SSAction : ScriptableObject{}
简单动作实现类: public class SSMoveToAction : SSAction {}
组合动作实现类: public class SequenceAction: SSAction, ISSActionCallback{}
动作管理基类:public class SSActionManager: MonoBehaviour, ISSActionCallback{}
以及,我们还要建立一个动作事件接口:public interface ISSActionCallback{}
动作管理
定义动作基类:使用 virtual 申明虚方法,通过重写实现多态。这样继承者就明确使用Start 和 Update 编程游戏对象行为,利用接口实现消息通知,避免与动作管理者直接依赖。

简单动作实现:让 Unity 创建动作类,确保内存正确回收。使用多态,C++ 语言必申明重写,Java则默认重写。

组合动作实现:创建一个动作顺序执行序列,-1 表示无限循环,start 开始动作。然后执行当前动作。收到当前动作执行完成,推下一个动作,如果完成一次循环,减次数。如完成,通知该动作的管理者。

动作接口定义:定义了事件处理接口,所有事件管理者都必须实现这个接口,来实现事件调度。所以,组合事件需要实现它,事件管理器也必须实现它。

动作管理基类:创建 MonoBehaiviour 管理一个动作集合,动作做完自动回收动作。该类演示了复杂集合对象的使用。提供了运行一个新动作的方法。该方法把游戏对象与动作绑定,并绑定该动作事件的消息接收者。执行改动作的 Start 方法。

动作分离版的不同- 新建裁判类,相当于将牧师与魔鬼中判断游戏结束 胜负状态的逻辑函数分离出来单独成一个类,用SSDirect实例化场景控制器载入Start函数,之后在FirstController 主控制器中调用Check的CheckGame判断游戏状态即可。
Check.cs 裁判类

using UnityEngine;

namespace CheckApplication{
    public class Check : MonoBehaviour
    {
        public FirstController sceneController;

        void Start()
        {
            sceneController = (FirstController)SSDirect.getInstance().CurrentSceneController;
            sceneController.gameStatusManager=this;
        }

        public int CheckGame(){
            //0-游戏1继续;1-游戏成功;-1-游戏失败
            int[] boatRole=sceneController.boat.getRoleNum();
            int[] startRole=sceneController.startLand.getRoleNum();
            int[] endRole=sceneController.endLand.getRoleNum();

            if(endRole[0]+endRole[1]==6) return 1;

            if(sceneController.boat.getBoatSign()==1){
                startRole[0]+=boatRole[0];
                startRole[1]+=boatRole[1];
            }else{
                endRole[0]+=boatRole[0];
                endRole[1]+=boatRole[1];
            }

            if((endRole[0]>0 && endRole[1]>endRole[0]) || (startRole[0]>0 && startRole[1]>startRole[0]))
                return -1;
            return 0;
        }
    }
}

其余代码逻辑与牧师与魔鬼基本差不多,资源此处我也使用作业三的预制体,详情参考之前的博客
https://blog.csdn.net/ex__plorer/article/details/127464505
``

其余类参考代码如下:

ActionController.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; //动作对象的transform
    public ISSActionCallback callback; //回调函数

    /*防止用户自己new对象*/
    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) {
        //unity创建
        SSMoveToAction action = ScriptableObject.CreateInstance<SSMoveToAction>();
        action.target = _target;
        action.speed = _speed;
        return action;
    }

    public override void Start() {
        //重写
    }

    public override void Update() {
        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) {
        //unity创建
        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 FirstController sceneController;
    private SequenceAction boatMove;
    private SequenceAction roleMove;

    protected void Start() {
        sceneController = (FirstController)SSDirect.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);
    }
}

点击事件 ClickeController.cs

using UnityEngine;
using ControllerApplication;
using InterfaceApplication;

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(){
        action=SSDirect.getInstance().CurrentSceneController as IUserAction;
    }
    void OnMouseDown(){
        if(boat==null && role==null) return;

        if(boat!=null) action.moveBoat();
        else if(role!=null) action.moveRole(role);
    }
}

FirstController .cs 载入资源等的基本控制器

using UnityEngine.SceneManagement;
using UnityEngine;
using ControllerApplication;
using InterfaceApplication;
using CheckApplication;

public class FirstController : MonoBehaviour,ISceneController,IUserAction{
    public LandModel startLand;
    public LandModel endLand;
    public Water water;
    public BoatModel boat;
    public RoleModel[] roles;
    public UserGUI GUI;
    public Check gameStatusManager;
    public ActionManager actionManager;

    void Start(){
        SSDirect director=SSDirect.getInstance();
        director.CurrentSceneController=this;
        GUI=gameObject.AddComponent<UserGUI>() as UserGUI;
        actionManager=gameObject.AddComponent<ActionManager>() as ActionManager;
        gameStatusManager=gameObject.AddComponent<Check>() as Check;
        LoadResoureces();
    }

    public void LoadResoureces(){
        water=new Water();//构建水
        startLand=new LandModel("start");//构建两岸
        endLand=new LandModel("end");
        boat=new BoatModel();//构建船
        roles=new RoleModel[6];//构建角色
        for(int i=0;i<3;i++) {
            RoleModel role=new RoleModel("priest", startLand.getEmptyPos());
            role.setName("priest"+i);
            startLand.addRole(role);
            roles[i]=role;
        }
        for(int i=0;i<3;i++) {
            RoleModel role=new RoleModel("devil", startLand.getEmptyPos());
            role.setName("devil"+i);
            startLand.addRole(role);
            roles[i+3]=role;
        }
    }
    public void moveBoat(){
        if (boat.empty() || GUI.sign!=0) return;
        Vector3 endPos;
        boat.changeBoatSign();
        if(boat.getBoatSign()==-1) endPos=new Vector3(-5,0.5f,0);
        else endPos=new Vector3(5,0.5f,0);
        actionManager.moveBoat(boat.getBoat(),endPos,boat.boatSpeed);
        GUI.sign=gameStatusManager.CheckGame();
    }
    public void moveRole(RoleModel role){
        if(GUI.sign!=0) return;
        Vector3 middlePos,endPos;
        if(role.getOnBoat()==1) {
            boat.deleteRole(role.getName());
            role.setOnBoat(0);
            role.getRole().transform.parent=null;
            if(boat.getBoatSign()==1) {
                endPos=startLand.getEmptyPos();
                role.setLandSign(1);
                startLand.addRole(role);
            }
            else {
                endPos=endLand.getEmptyPos();
                role.setLandSign(-1);
                endLand.addRole(role);
            }
            middlePos=new Vector3(role.getRole().transform.position.x,endPos.y,endPos.z);
            actionManager.moveRole(role.getRole(),middlePos,endPos,role.roleSpeed);  
        }else{
            if(role.getLandSign()==1) {
                if(boat.getEmptyNumber()==-1 || role.getLandSign()!=boat.getBoatSign()) return;
                startLand.deleteRole(role.getName()); 
            }else{
                if(boat.getEmptyNumber() == -1 || role.getLandSign()!=boat.getBoatSign()) return;
                endLand.deleteRole(role.getName());
            }
            endPos=boat.getEmptyPos();
            middlePos=new Vector3(endPos.x,role.getRole().transform.position.y,endPos.z);
            actionManager.moveRole(role.getRole(),middlePos,endPos,role.roleSpeed);
            role.getRole().transform.parent=boat.getBoat().transform;
            role.setOnBoat(1);
            boat.addRole(role);
        }
        GUI.sign=gameStatusManager.CheckGame();
    }
    public void reStart(){
        SceneManager.LoadScene(0);
    }

}

``场景接口类 Interface.cs

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

namespace  InterfaceApplication{
    //场景接口
    public interface ISceneController{
        void LoadResoureces();
    }

    //用户动作接口
    public interface IUserAction{
        void moveBoat();
        void moveRole(RoleModel role);
        void reStart();
    }

    public enum SSActionEventType: int{started,competeted}
    public interface ISSActionCallback{
        void SSActionEvent(
            SSAction source,SSActionEventType events=SSActionEventType.competeted,
            int intParam=0,string strParam=null,Object ObjectParam=null
        );
    }

ModelController.cs

using UnityEngine;

namespace ControllerApplication{
    public class Water{
        GameObject water;
        public Water(){
            water=Object.Instantiate(Resources.Load<GameObject>("Prefabs/Water"),
                Vector3.zero, Quaternion.identity) as GameObject;
            water.name="water";
        }
    }

    public class LandModel{
        GameObject land;
        int landSign;
        Vector3[] positions;
        RoleModel[] roles = new RoleModel[6];

        public LandModel(string str){
            positions=new Vector3[] {
                new Vector3(6.35F,2F,0), new Vector3(7.35f,2F,0), new Vector3(8.35f,2F,0),
                new Vector3(9.35f,2F,0), new Vector3(10.35f,2F,0), new Vector3(11.35f,2F,0)
            };
            if (str=="start") landSign=1;
            else landSign=-1;
            land=Object.Instantiate(Resources.Load<GameObject>("Prefabs/Land"),
                    new Vector3(9*landSign,1,0), Quaternion.identity) as GameObject;
            land.name =str+"Land";
        }

        //一系列用于处理角色上岸和上船的成员函数
        public int getLandSign(){
            return landSign;
        }
        public int getEmptyNumber(){
            for(int i=0;i<6;i++){
                if(roles[i]==null)
                    return i;
            }
            return -1;
        }
        public Vector3 getEmptyPos(){
            Vector3 res=positions[getEmptyNumber()];
            res.x=res.x*landSign;
            return res;
        }
        public int[] getRoleNum(){
            int[] res={0,0};
            for(int i=0;i<6;i++){
                if(roles[i]!=null){
                    if(roles[i].getSign()==0) res[0]++;
                    else res[1]++;
                }
            }
            return res;
        }

        public void addRole(RoleModel role){
            roles[getEmptyNumber()]=role;
        }
        public RoleModel deleteRole(string name){
            for(int i=0;i<6;i++){
                if(roles[i]!=null && roles[i].getName()==name){
                    RoleModel res=roles[i];
                    roles[i]=null;
                    return res;
                }
            }
            return null;
        }
    }

    public class BoatModel{
        GameObject boat;
        int boatSign;
        Vector3[] startPos,endPos;
        RoleModel[] roles=new RoleModel[2];
        Click click;
        public float boatSpeed=40f;

        public BoatModel(){
            boat=Object.Instantiate(Resources.Load<GameObject>("Prefabs/Boat"), 
                new Vector3(5,0.5f,0), Quaternion.identity) as GameObject;
            boatSign=1;
            startPos=new Vector3[]{ new Vector3(5.5f,1,0), new Vector3(4.5f,1,0) };
            endPos = new Vector3[]{ new Vector3(-4.5f,1,0), new Vector3(-5.5f,1,0) };
            click=boat.AddComponent(typeof(Click)) as Click;
            click.setBoat(this);
            boat.name="boat";
        }

        //一系列用于处理角色上岸和上船以及船移动的成员函数
        public void changeBoatSign(){
            boatSign*=-1;
        }

        public int getBoatSign(){
            return boatSign;
        }
        public int getEmptyNumber(){
            for(int i=0;i<2;i++){
                if(roles[i]==null)
                    return i;
            }
            return -1;
        }
        public Vector3 getEmptyPos(){
            Vector3 res;
            if(boatSign==1) res=startPos[getEmptyNumber()];
            else res=endPos[getEmptyNumber()];
            return res;
        }
        public int[] getRoleNum(){
            int[] res={0,0};
            for(int i=0;i<2;i++){
                if(roles[i]!=null){
                    if(roles[i].getSign()==0) res[0]++;
                    else res[1]++;
                }
            }
            return res;
        }

        public void addRole(RoleModel role){
            roles[getEmptyNumber()]=role;
        }
        public RoleModel deleteRole(string name){
            for(int i=0;i<2;i++){
                if(roles[i]!=null && roles[i].getName()==name){
                    RoleModel res=roles[i];
                    roles[i]=null;
                    return res;
                }
            }
            return null;
        }

        public GameObject getBoat(){
            return boat;
        }

        public bool empty(){
            for(int i=0;i<2;i++){
                if(roles[i]!=null) return false;
            }
            return true;
        }
    }

    public class RoleModel{
        GameObject role;
        int roleSign;
        int onBoat;
        int landSign;
        Click click;
        public float roleSpeed=20f;

        public RoleModel(string name,Vector3 pos){
            if(name=="priest") {
                role=Object.Instantiate(Resources.Load<GameObject>("Prefabs/Priest"),
                    pos, Quaternion.Euler(0,-90,0)) as GameObject;
                roleSign=0;
            }else{
                role=Object.Instantiate(Resources.Load<GameObject>("Prefabs/Devil"),
                    pos, Quaternion.Euler(0,-90,0)) as GameObject;
                roleSign=1;
            }
            landSign=1;
            click=role.AddComponent(typeof(Click)) as Click;
            click.setRole(this);
        }

        public string getName(){ 
            return role.name; 
        }
        public int getLandSign(){ 
            return landSign; 
        }
        public void setLandSign(int land){ 
            landSign=land; 
        }
        public int getSign(){
            return roleSign;
        }
        public int getOnBoat(){ 
            return onBoat; 
        }
        public void setOnBoat(int a){ 
            onBoat=a; 
        }
        public void setName(string name){ 
            role.name=name; 
        }
        public void setPosition(Vector3 pos) {
            role.transform.position = pos;
        }
        public GameObject getRole(){
            return role;
        }

    }
}

``导演类SSDirect.cs

using UnityEngine;
using ControllerApplication;
using InterfaceApplication;

public class SSDirect : System.Object{
    private static SSDirect instance;
    public ISceneController CurrentSceneController{ get; set;}

    public static SSDirect getInstance(){
        if(instance==null) instance=new SSDirect();
        return instance;
    }
}

用户界面 UserGUI.cs

using UnityEngine;
using InterfaceApplication;

public class UserGUI : MonoBehaviour
{
    public IUserAction action;
    public int sign=0;
    void Start() {
        action = SSDirect.getInstance().CurrentSceneController as IUserAction;
    }
    void OnGUI() {
        GUIStyle text_style;
        GUIStyle button_style;
        text_style = new GUIStyle() {
            fontSize = 30
        };
        button_style = new GUIStyle("button") {
            fontSize = 15
        };

        if(sign==-1) {
            GUI.Label(new Rect(Screen.width/2-90,Screen.height/2-120,100,50),"Gameover!",text_style);  
        }
        else if(sign==1) {
            GUI.Label(new Rect(Screen.width/2-80,Screen.height/2-120,100,50),"You Win!",text_style);
        }

        if (GUI.Button(new Rect(Screen.width/2-50,Screen.height/2-200,100,50),"Restart",button_style)) {
            action.reStart();
            sign = 0;
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值