四、游戏对象与图形基础

1、基本操作演练【建议做】

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

  • 下载 Fantasy Skybox FREE
    在这里插入图片描述

  • 给主摄像机添加skybox组件
    在这里插入图片描述

  • 将skybox添加到场景

  1. 从菜单栏中选择 Window > Rendering > Lighting > Environment。
  2. 把天空盒拖到天空盒材质中
    在这里插入图片描述
  • 构建一个地形
    在这里插入图片描述
  • 最终效果
    在这里插入图片描述

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

创建

  1. 通过图形界面创建

  2. 使用GameObject.CreatPrimitive()函数来创建Unity自带的模型

GameObject obj = GameObject.CreatePrimitive(PrimitiveType.Cube);
  1. 使用Instantiate()函数进行游戏物体的实例化
Instantiate(GameObject,Position,Rotition)
Instantiate(brick, new Vector3(x, y, 0), Quaternion.identity);
  1. 动态加载预制。将预制件放在目录Resouces\下在脚本中通过Resources.Load加载:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class LoadCube : MonoBehaviour {
 
	// Use this for initialization
	void Start () {
		GameObject hp_bar = (GameObject)Resources.Load("Cube");
		hp_bar = Instantiate(hp_bar); 
		hp_bar.name = "Cube";
	}
	
	// Update is called once per frame
	void Update () {
		
	}
}

获取

  1. 在代码里声明public类型Gameobject,在Inspector属性栏中指定游戏对象
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class Find_GameObject : MonoBehaviour {
 
    public GameObject obj;
}

在这里插入图片描述

  1. 通过对象名称获取对象
obj = GameObject.Find("Sphere");
  1. 通过对象tag获取对象
GameObject.FindWithTag("Atag")                         //返回一个游戏对象
GameObject.FindGameObjectsWithTag("Atag")    //返回多个游戏对象

修改属性

gameobject.transform.position = new Vector3(2, (float)-0.5, 0);

添加组件与修改组件

  1. 在inspector界面手动添加
    在这里插入图片描述

  2. 通过脚本添加,并设定组件中的某些值

cube.AddComponent<Rigidbody>();
cube.GetComponent<Rigidbody>().useGravity = false;
cube.GetComponent<Renderer>().material.color = Color.red;

克隆

克隆产生的新的对象会具有原对象的所有属性与组件
原对象被保存为文件类型则称为预制体

GameObject hp_bar = (GameObject)Resources.Load("Cube"); //动态加载预制
hp_bar = Instantiate(hp_bar); //克隆

2、编程实践

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

动作分离

在此前版本中,使用抽象出来的一个类Move挂载脚本组件,实现角色的移动功能(上船、下船)和船的移动功能(开向对岸)
现在希望将动作分离,由FirstController直接管理所有的动作,而在BoatController和CharacterController中只需处理好自己的逻辑关系
在这里插入图片描述

新增如下类

  • IActionCallback
    一个接口。接口回调(函数回调)实现管理者与被管理者解耦。实现了这个接口的类,就可以知道“某个动作已完成”(动作一完成ActionEvent方法就会被调用),并对这个事件做出反应。

  • Action
    所有动作的基类,包括单个直线action和复合action。

  • MoveToAction
    Action的一个子类。基本动作,用户设计的基本动作类。表示直线移动。

  • SequenceAction
    Action的另一个子类。合动作,由(基本或组合)动作组合的类。表示多个直线移动组成的复合动作。

  • SSActionManager
    代理Action,实现了所有动作的基本管理,在管理动作的时候不用关心是直线还是复合动作,一并处理。【组合模式】

  • CCActionManager
    SSActionManager的子类,封装了一些函数,使得FirstController调用起来更简洁。通过模板方法,让使用者减少对动作管理过程细节的要求。【门面模式(控制器模式)】

现对BoatController和CharactersController进行修改,修改后的BoatController和CharactersController不再管理和使用move

BoatController

public class Move : MonoBehaviour
{
    public float moveSpeed = 20;
    //public Move move;
    ...
    /* public void moveToAnotherBank()
     {
         if (boatState == 1)
         {
             Vector3 target = boat.transform.position + new Vector3(8, 0, 0);
             move.setDestination(target);
             this.boatState = 2;
         }
         else if (boatState == 2)
         {
             Vector3 target = boat.transform.position - new Vector3(8, 0, 0);
             move.setDestination(target);
             this.boatState = 1;
         }
     }
    */
    public Vector3 getDestination()
    {
        Vector3 target = Vector3.zero;
        if (boatState == 1)
        {
            target = boat.transform.position + new Vector3(8, 0, 0);
            this.boatState = 2;
        }
        else if (boatState == 2)
        {
            target = boat.transform.position - new Vector3(8, 0, 0);
            this.boatState = 1;
        }
        return target;
    }
    public GameObject getGameobj()
    {
        return this.boat;
    }

}

CharactersController

public class CharacterController
{
	public float moveSpeed = 20;
    //public Move move;
    ...
    public void setPositionOnLeftBank()
    {
        this.state = 1;
        //move.setDestination(new Vector3(-20 + index*2, 2f, 0));
    }

    public Vector3 getPosOnLeftBank()
    {
        return new Vector3(-20 + index * 2, 2f, 0);
    }

    public void setPositionOnRightBank()
    {
        this.state = 2;
        //move.setDestination(new Vector3(10 + index*2, 2f, 0));
    }

    public Vector3 getPosOnRightBank()
    {
        return new Vector3(10 + index * 2, 2f, 0);
    }
    public void setPositionOnBoat()//Vector3 pos
    {
        this.state = 0;
        //move.setDestination(pos);
    }
/*
    public void moveOnBoat(int boatState, int posOnBoat)
    {
        if (boatState == 1)
        {
            Vector3 target = character.transform.position + new Vector3(8, 0, 0);
            move.setDestination(target);
        }
        else if (boatState == 2)
        {
            Vector3 target = character.transform.position + new Vector3(-8, 0, 0);
            move.setDestination(target);
        }
    }
*/
    public Vector3 getDestinationOnBoat(int boatState, int posOnBoat)
    {
        if (boatState == 1)
        {
            return character.transform.position + new Vector3(8, 0, 0);
        }
        else if (boatState == 2)
        {
            return character.transform.position - new Vector3(8, 0, 0);
        }
        return Vector3.zero;
    }

    public Vector3 getPos()
    {
        return this.character.transform.position;
    }

    public GameObject getGameobj()
    {
        return this.character;
    }

FirstController
不需要实例化Move,用CCActionManager管理动作

	public class FirstController : MonoBehaviour, ISceneController, IUserAction {
		private CCActionManager myActionManager;
		public void LoadResources () {
			myActionManager = gameObject.AddComponent<CCActionManager>() as CCActionManager;
	}
	public void moveBoat()
    {
		if (boat.getNumEmptyPos() == 2) return;       //没人在船上,船不动
		int[] personOnBoat = boat.getPersonOnBoat();  //得到在船上的人的编号
		//boat.moveToAnotherBank();
		myActionManager.moveBoat(boat);
		for (int i = 0; i < 2; i++)
		{
			if (personOnBoat[i] == -1) continue;
			//characters[personOnBoat[i]].moveOnBoat((boat.getState() == 1 ? 2 : 1), i);   //先移动船再移动人,船的state先变,让char得到移动后的state
			myActionManager.moveCharacter(characters[personOnBoat[i]], characters[personOnBoat[i]].getDestinationOnBoat((boat.getState() == 1 ? 2 : 1), i));
		}

		if (judgment.checkLose(boat, leftBank, rightBank) == true)
		{
			for(int i = 0; i < 7; i++)
			{
				clickGUI[i].click = false;

			}
			userGUI.gameState = -1;
			return;
		}
	}
	public void moveCharacter(int index)
    {
		if (characters[index].getState() == 1 && boat.getState() == 1 && boat.getNumEmptyPos() > 0)   //人在左岸,船在左岸,船有空位
		{
			characters[index].setPositionOnBoat();
			myActionManager.moveCharacter(characters[index], boat.getEmptyPos());  ;
			leftBank.removeACharacter(characters[index]);
			boat.putACharacter(characters[index]);
		}
		else if (characters[index].getState() == 2 && boat.getState() == 2 && boat.getNumEmptyPos() > 0)   //人在右岸,船在右岸,船有空位
		{
			characters[index].setPositionOnBoat();
			myActionManager.moveCharacter(characters[index], boat.getEmptyPos());   
			rightBank.removeACharacter(characters[index]);
			boat.putACharacter(characters[index]);
		}
		else if (characters[index].getState() == 0 && boat.getState() == 1)       //人在船上,船靠左岸
		{
			characters[index].setPositionOnLeftBank();
			myActionManager.moveCharacter(characters[index], characters[index].getPosOnLeftBank());   
			boat.removeACharacter(characters[index]);
			leftBank.putACharacter(characters[index]);
		}
		else if (characters[index].getState() == 0 && boat.getState() == 2)       //人在船上,船靠右岸
		{
			characters[index].setPositionOnRightBank();
			myActionManager.moveCharacter(characters[index], characters[index].getPosOnRightBank()); 
			boat.removeACharacter(characters[index]);
			rightBank.putACharacter(characters[index]);
		}

		if (judgment.checkWin(boat, leftBank, rightBank) == true)
		{
			for (int i = 0; i < 7; i++)
			{
				clickGUI[i].click = false;

			}
			userGUI.gameState = 1;
			return;
		}
	}
	

裁判类

FirstController需要管理的事情太多,所以需要将判断游戏输赢的任务分离出来,交给单独的一个类管理,在前一个作业的基础上简单改一下即可。

Judgment
把之前用来判断游戏输赢的checkWincheckLose函数分离出来,单独放在一个类Judgment中。

由于这些函数需要boatleftBankrightBank的信息,所以需要传入相应的controller作为参数。

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

public class Judgment
{
    public bool checkWin(BoatController boat, BankController leftBank, BankController rightBank)
    {
        if (boat.getNumEmptyPos() == 2 && (leftBank.getNumDevil() + leftBank.getNumPriest() == 0) && (rightBank.getNumDevil() + rightBank.getNumPriest() == 6))
            return true;
        return false;
    }

    public bool checkLose(BoatController boat, BankController leftBank, BankController rightBank)
    {
        int countDevilLeft = leftBank.getNumDevil(), countPriestLeft = leftBank.getNumPriest();
        int countDevilRight = rightBank.getNumDevil(), countPriestRight = rightBank.getNumPriest();
        int[] personOnBoat = boat.getPersonOnBoat();   
        int d = 0, p = 0;
        for (int i = 0; i < 2; i++)
        {
            if (personOnBoat[i] < 3 && personOnBoat[i] >= 0) d++;
            else if (personOnBoat[i] >= 3) p++;
        }
        if (boat.getState() == 1)
        {
            countDevilLeft += d;
            countPriestLeft += p;
        }
        else if (boat.getState() == 2)
        {
            countDevilRight += d;
            countPriestRight += p;
        }
        if ((countDevilLeft > countPriestLeft && countPriestLeft != 0) || (countDevilRight > countPriestRight && countPriestRight != 0))
        {
            return true;
        }
        return false;
    }
}

FirstController
增加一个类型为Judgment的变量judgment,在loadResource中实例化
在原来使用checkWincheckLose的地方改成judgment.checkWinjudgment.checkLose

public Judgment judgment;
	
public void loadResources()
{
	...
	judgment = new Judgment();
}
public void moveBoat()
{
	...
	if (judgment.checkLose(boat, leftBank, rightBank) == true)
	{
		...
	}
}
public void moveCharacter(int index)
{
	...
	if (judgment.checkWin(boat, leftBank, rightBank) == true)
	{
		...
	}
}

代码传送门

效果与上一次的代码相同
失败
在这里插入图片描述
成功
在这里插入图片描述

处理历史遗留问题(注意细节,例如:船未靠岸,牧师与魔鬼上下船运动中,均不能接受用户事件!

SSDirector.cs
增加变量

public bool moving=false;

仅当变量moving为false时接收用户事件
UserGUI.cs

    void OnMouseDown()
    {
        if (click&&!SSDirector.getInstance().moving)
        {
        //接收用户事件并处理
        }
    }

动作开始时设置moving为true,动作完成后设置moving为false
CCActionManager.cs

    public void moveBoat(BoatController boat)
    {
        MoveToAction action = MoveToAction.getAction(boat.getDestination(), boat.moveSpeed);
        this.RunAction(boat.getGameobj(), action, this);
        SSDirector.getInstance().moving = true;
    }

    public void moveCharacter(CharacterController characterCtrl, Vector3 destination)
    {
        ...
        this.RunAction(characterCtrl.getGameobj(), seqAction, this);
        SSDirector.getInstance().moving = true;
    }
    public void ActionEvent(Action source, ActionEventType events = ActionEventType.Competeted)
    {
        if (events == ActionEventType.Competeted) SSDirector.getInstance().moving = false;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值