文章目录
1、基本操作演练【建议做】
1.1 下载 Fantasy Skybox FREE, 构建自己的游戏场景
-
下载 Fantasy Skybox FREE
-
给主摄像机添加skybox组件
-
将skybox添加到场景
- 从菜单栏中选择 Window > Rendering > Lighting > Environment。
- 把天空盒拖到天空盒材质中
- 构建一个地形
- 最终效果
1.2 写一个简单的总结,总结游戏对象的使用
创建
-
通过图形界面创建
-
使用GameObject.CreatPrimitive()函数来创建Unity自带的模型
GameObject obj = GameObject.CreatePrimitive(PrimitiveType.Cube);
- 使用Instantiate()函数进行游戏物体的实例化
Instantiate(GameObject,Position,Rotition)
Instantiate(brick, new Vector3(x, y, 0), Quaternion.identity);
- 动态加载预制。将预制件放在目录
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 () {
}
}
获取
- 在代码里声明public类型Gameobject,在Inspector属性栏中指定游戏对象
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Find_GameObject : MonoBehaviour {
public GameObject obj;
}
- 通过对象名称获取对象
obj = GameObject.Find("Sphere");
- 通过对象tag获取对象
GameObject.FindWithTag("Atag") //返回一个游戏对象
GameObject.FindGameObjectsWithTag("Atag") //返回多个游戏对象
修改属性
gameobject.transform.position = new Vector3(2, (float)-0.5, 0);
添加组件与修改组件
-
在inspector界面手动添加
-
通过脚本添加,并设定组件中的某些值
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
把之前用来判断游戏输赢的checkWin
和checkLose
函数分离出来,单独放在一个类Judgment
中。
由于这些函数需要boat
,leftBank
,rightBank
的信息,所以需要传入相应的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
中实例化
在原来使用checkWin
和checkLose
的地方改成judgment.checkWin
和judgment.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;
}