Unity3D-魔鬼与牧师游戏(动作分离版)开发

我们对Unity3D-魔鬼与牧师游戏开发-优快云博客 进行进一步改进。

目录

一、动作管理器

1、为什么要引入动作管理器

2、什么是动作管理器

二、实现思路

三、面向对象设计UML图

四、动作分离

五、裁判类

六、加载资源、点击事件以及用户GUI

七、游戏运行效果展示

一、动作管理器

1、为什么要引入动作管理器

在一个场景中肯定有很多“会动”的物体,它们的运动是有很多共性的,如果我们为游戏角色实现一个运动方法,为船实现一个运动方法,为将来出现的所有会动的物体都实现一个运动方法,势必是一种资源的浪费。我们可以将运动的共性提取出来,用一个管理器统一管理,这样,代码的复用性和可读性都会提高。

2、什么是动作管理器

  • 动作管理器就是一个对象,管理整个场景所有的动作
  • 一个SceneController(场景管理器)只配备一个动作管理器对象。
  • 不管是游戏角色的移动还是船的移动,都归这个对象管;
  • 动作管理器可以添加动作(添加的时候要指定动作所作用的GameObject),监测已经完成的动作并清除。

二、实现思路

  • 设计一个抽象类作为游戏对象动作的基类
  • 设计一个动作管理器类管理一组游戏动作的实现类
  • 通过回调,实现动作完成时的通知
  • 设计一个裁判类,当游戏达到结束条件时,通知场景控制器游戏结束

将移动动作的执行从每一个游戏对象中提取出来,建立一个动作管理器来管理移动方法,游戏对象通过将目标位置传给动作管理器,让动作管理器来移动游戏对象
将游戏的判定机制抽象成一个类,通过调用该类的方法来判定游戏是否结束
这使得:

  1. 程序的解耦合程度提高
  2. 更多对象可以复用
  3. 程序更容易维护

三、动作分离

  在上次实验中,游戏对象的移动由MoveController和Move共同管理,这样做的坏处是在FirstController中仍有管理对象运动的代码。在动作分离版的代码中,管理动作的代码被分解成以下三部分,CCActionManager用于管理所有动作,CCMoveAction用于管理“移动”这种动作,Move则是移动这个动作的实体。

1、 CCActionManager

主要实现了MoveRole()和MoveBoat()两个函数。通过调用CCMoveAction中的MoveTo和MoveSeqcenceTo来实现两种不同形式的移动效果。

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

public class CCActionManager
{
    public CCMoveAction moveBoatAction;
    public CCMoveAction moveRoleAction;

    public FirstController controller;

    public CCActionManager(){
        controller = SSDirector.GetInstance().CurrentSceneController as FirstController;
        controller.actionManager = this;

        moveBoatAction = new CCMoveAction();
        moveRoleAction = new CCMoveAction();
    }


    public bool IsMoving(){
        return moveRoleAction.IsMoving() || moveBoatAction.IsMoving();
    }


    public void MoveRole(BoatController BoatCtrl, RoleController RoleCtrl, int destination, int seat){
        Vector3 finalPos;
        if(destination == FirstController.RIGHTLAND){
            finalPos = Position.roleRightPos[seat];
        }
        else if(destination == FirstController.LEFTLAND){
            finalPos = Position.roleLeftPos[seat];
        }
        else{
            if(BoatCtrl.onLeftside){
                finalPos = Position.seatLeftPos[seat];
            }
            else{
                finalPos = Position.seatRightPos[seat];
            }
        }
        moveRoleAction.MoveSequenceTo(RoleCtrl.GetModelGameObject(), finalPos);

    }


    public void MoveBoat(BoatController BoatCtrl, int destination){
        if(destination == FirstController.RIGHTLAND){
            moveBoatAction.MoveTo(BoatCtrl.GetModelGameObject(), Position.boatRightPos);
            for(int i = 0; i < 3; i++){
                if(BoatCtrl.seat[i] != -1){
                    RoleController r = controller.RoleCtrl[controller.IDToNumber(BoatCtrl.seat[i])];
                    moveRoleAction.MoveTo(r.GetModelGameObject(), Position.seatRightPos[i]);
                }
            }
        }
        else{
            moveBoatAction.MoveTo(BoatCtrl.GetModelGameObject(), Position.boatLeftPos);
            for(int i = 0; i < 3; i++){
                if(BoatCtrl.seat[i] != -1){
                    RoleController r = controller.RoleCtrl[controller.IDToNumber(BoatCtrl.seat[i])];
                    moveRoleAction.MoveTo(r.GetModelGameObject(), Position.seatLeftPos[i]);
                }
            }
        }
    }
}

2、  CCMoveAction

给特定的gameobject添加Move脚本并且设置脚本的属性。

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

public class CCMoveAction
{
    GameObject moveObject;
    
    public bool IsMoving(){
        return(this.moveObject != null && this.moveObject.GetComponent<Move>().isMoving == true);
    }

    public void MoveTo(GameObject moveObject, Vector3 destination){
        Move test;
        this.moveObject = moveObject;
        if (!moveObject.TryGetComponent<Move>(out test)) {
            moveObject.AddComponent<Move>();
        }
        this.moveObject.GetComponent<Move>().moveAction = this;
        this.moveObject.GetComponent<Move>().destination = destination;
        this.moveObject.GetComponent<Move>().moveMode = Move.single;

    }

    public void MoveSequenceTo(GameObject moveObject, Vector3 destination){
        Move test;
        this.moveObject = moveObject;
        if (!moveObject.TryGetComponent<Move>(out test)) {
            moveObject.AddComponent<Move>();
        }
        this.moveObject.GetComponent<Move>().moveAction = this;
        this.moveObject.GetComponent<Move>().destination = destination;
        this.moveObject.GetComponent<Move>().moveMode = Move.sequence;
    }
}

3、 Move 

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

public class Move : MonoBehaviour
{
    public static int single = 0;
    public static int sequence = 1;


    public bool isMoving;
    bool initialized;
    public int moveMode;
    public bool doneMoving;

    public float speed = 5;


    int n_seq;
    public Vector3[] desseq;
    public Vector3 destination;
    public CCMoveAction moveAction;


    public Move(){
        n_seq = 0;
        isMoving = false;
        initialized = false;
        moveMode = -1;
    }

    void Update()
    {
        if(moveMode == -1) return;
        if(!initialized){
            if(moveMode == single){
                desseq = new Vector3[1];
                desseq[0] = destination;
            }
            else if(moveMode == sequence){
                desseq = new Vector3[3];
                desseq[0] = transform.localPosition + new Vector3(0, 1, 0);
                desseq[1] = destination + new Vector3(0, 1, 0);
                desseq[2] = destination;
            }
            else{
                Debug.Log("ERROR!");
            }
            initialized = true;
        }     
        isMoving = true;
        if(n_seq >= desseq.Length){
            n_seq = 0;
            moveMode = -1;
            initialized = false;
            isMoving = false;
            return;
        }
        if(transform.localPosition == desseq[n_seq]){
            n_seq += 1;
            return;
        }
        transform.localPosition = Vector3.MoveTowards(transform.localPosition, desseq[n_seq], speed * Time.deltaTime);
    }
}

四、裁判类

  实现一个裁判类,控制器初始化时将游戏对象注入到裁判类中,裁判类通过游戏规则判断游戏是否结束。而通过调用裁判类来通知场景控制器,而场景控制器又通知UI,在UI中查看游戏状态判断是否结束游戏即可

  创建一个裁判类,把原来写在FirstController中的GetAndSetGameState函数迁移到裁判类中,形成了裁判类的UpdateGameState方法。原来的场景控制器FirstController需要在Awake和初始化函数中初始化一个裁判类,现在将原来调用GetAndSetGameState函数的地方修改成请求裁判类裁决。裁判类的代码如下:

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

public class JudgeController{

    FirstController firstCtrl;

    public JudgeController(){
        firstCtrl = SSDirector.GetInstance().CurrentSceneController as FirstController;
    }

    //判断游戏状态
    public int UpdadeGameState(){
        if(firstCtrl.gameState != FirstController.PLAYING) return firstCtrl.gameState;
        //判断是否失败
        int[,] rolePos = new int[2, 3]{{0, 0, 0}, {0, 0, 0}};
        foreach(RoleController r in firstCtrl.RoleCtrl){
            rolePos[r.roleType, r.roleState]++;
        }
        if((rolePos[0,0]>0 && rolePos[0,0]<rolePos[1,0]) || 
           (rolePos[0,1]>0 && rolePos[0,1]<rolePos[1,1]) || 
           (rolePos[0,2]>0 && rolePos[0,2] < rolePos[1,2])){
            return FirstController.FAILED;
        }  
        //判断是否成功
        foreach(RoleController r in firstCtrl.RoleCtrl){
            if(r.roleType == 0 && r.roleState != FirstController.RIGHTLAND){
                return FirstController.PLAYING; 
            }
        }
        return FirstController.WIN;
    }
}

五、加载资源、点击事件以及用户GUI

  这几部分与上次实验基本一致。

六、面向对象设计UML图

七、游戏运行效果展示

  将Assert/Scripts/UserGUI拖动到Main Camera上,创建一个新的空GameObject,将FirstController拖动到GameObject上,点击运行即可。

视频展示:Devil and Priest_哔哩哔哩_bilibili

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值