基本操作演练
下载 Fantasy Skybox FREE, 构建自己的游戏场景
首先我们下载天空盒:
然后:Assets 上下文菜单 -> create -> Material 起名 mysky
在 Inspector 视图中选择 Shader -> Skybox -> 6Sided
在资源贴图中选择合适的图片,拖放到对应位置
在 Camera 对象中添加部件 Rendering -> Skybox
将天空盒拖放入 Skybox
运行游戏,看到下面的游戏画面:
天空盒应用成功。
除了天空盒,我们还可以添加Terrain,来改变地形,添加树木、花草等。如下图:
写一个简单的总结,总结游戏对象的使用
游戏对象主要包括以下几类:
- 常见游戏对象
如正方体,球体,平面等等,还有空对象,不显示却是最常用的对象之一。 - Camera 摄像机
它是观察游戏世界的窗口,Projection属性包括正交视图与透视视图。Viewport Rect:属性是控制摄像机呈现结果对应到屏幕上的位置以及大小。屏幕坐标系:左下角是(0, 0),右上角是(1, 1)。Depth属性是当多个摄像机同时存在时,这个参数决定了呈现的先后顺序,值越大越靠后呈现。 - 光源Light
灯光类型(type)包括平行光(类似太阳光),聚光灯(spot),点光源(point),区域光(area,仅烘培用)。 - 地形构造工具Terrain
可以构建自己的游戏场景
游戏对象中包含许多的组件,而正是这些组件使得游戏对象可以变得丰富多彩,例如:Transform决定游戏对象的位置、角度、对象。可通过C#动态改变Transform属性以实现物体的移动。通过改变物体的material可以赋予它们不同的外观。在制作游戏对象时,我们可以将它们做成预制,从而减少我们的工作量。
编程实践
牧师与魔鬼 动作分离版
- 【2019新要求】:设计一个裁判类,当游戏达到结束条件时,通知场景控制器游戏结束
在我上一次实现的代码中,动作是没有完成分离的,也就是说,代表人物和船的控制器的类里面,既包含了游戏对象的状态信息,也包含了它们动作的实现,包括人物的上船、下船以及船的移动。
我们先来看一下原来的控制器的实现(以人为例):
public class peopleController
{
GameObject people;
private string status;
public string size;
private string defaultSize;
firstScenceSolveClick solveClick;
int number;
public peopleController(string name, int number, Vector3 pos, string status, string size)
{
people = Object.Instantiate(Resources.Load("Prefabs/" + name, typeof(GameObject))
, pos, Quaternion.identity, null) as GameObject;
people.name = name + number.ToString();
solveClick = people.AddComponent(typeof(firstScenceSolveClick)) as firstScenceSolveClick;
solveClick.setName(people.name);
this.number = number;
this.status = status;
defaultSize = size;
this.size = size;
}
public string getName()
{
return people.name;
}
public string getStatus()
{
return status;
}
public void getOnBoat(boatController boatCtrl)
{
status = "boat";
people.transform.parent = boatCtrl.getBoat().transform;
people.transform.localPosition = boatCtrl.getBoatPos(getName());
}
public void getOffBoat(environmentController envCtrl)
{
status = "shore";
people.transform.parent = null;
people.transform.position = envCtrl.getPosVec(size, number);
}
public void reset(environmentController envCtrl)
{
status = "shore";
size = defaultSize;
people.transform.parent = null;
people.transform.position = envCtrl.getPosVec(size, number);
}
}
这里面是实现了上船和下船的动作。
我们下面要做的,是把这些动作的实现放在一个新的类中。于是我新创建了一个Action类,在里面实现这些动作,下面是Action类中上船、下船的实现:
public void getOnBoat(boatController boatCtrl,peopleController peopleCtrl)
{
peopleCtrl.status = "boat";
peopleCtrl.people.transform.parent = boatCtrl.getBoat().transform;
peopleCtrl.people.transform.localPosition = boatCtrl.getBoatPos(peopleCtrl.getName());
}
public void getOffBoat(environmentController envCtrl, peopleController peopleCtrl)
{
peopleCtrl.status = "shore";
peopleCtrl.people.transform.parent = null;
peopleCtrl.people.transform.position = envCtrl.getPosVec(peopleCtrl.size, peopleCtrl.number);
}
在这里完成了上船和下船,而新的人的控制器代码中的getOnBoat和getOffBoat函数删去(下面注释掉了这两个函数):
public class peopleController
{
public GameObject people;
public string status;
public string size;
private string defaultSize;
firstScenceSolveClick solveClick;
public int number;
public peopleController(string name, int number, Vector3 pos, string status, string size)
{
people = Object.Instantiate(Resources.Load("Prefabs/" + name, typeof(GameObject))
, pos, Quaternion.identity, null) as GameObject;
people.name = name + number.ToString();
solveClick = people.AddComponent(typeof(firstScenceSolveClick)) as firstScenceSolveClick;
solveClick.setName(people.name);
this.number = number;
this.status = status;
defaultSize = size;
this.size = size;
}
public string getName()
{
return people.name;
}
public string getStatus()
{
return status;
}
/*public void getOnBoat(boatController boatCtrl)
{
status = "boat";
people.transform.parent = boatCtrl.getBoat().transform;
people.transform.localPosition = boatCtrl.getBoatPos(getName());
}*/
/*public void getOffBoat(environmentController envCtrl)
{
status = "shore";
people.transform.parent = null;
people.transform.position = envCtrl.getPosVec(size, number);
}*/
public void reset(environmentController envCtrl)
{
status = "shore";
size = defaultSize;
people.transform.parent = null;
people.transform.position = envCtrl.getPosVec(size, number);
}
}
相应的,在场记的代码中,原来的getOnBoat和getOffBoat函数(在场记中是一个函数getBoatOrGetShore)的实现如下:
public void getBoatOrGetShore(string name)
{
if (myBoat.getRunningState() != "waiting")
{
return;
}
int numberOfPeople = name[name.Length - 1] - '0';
if (peopleCtrl[numberOfPeople].getStatus() == "shore")
{
if (myBoat.ifHaveSeat() && myBoat.size == peopleCtrl[numberOfPeople].size)
{
peopleCtrl[numberOfPeople].getOnBoat(myBoat);
}
}
else
{
if (myBoat.size == peopleCtrl[numberOfPeople].size)
{
peopleCtrl[numberOfPeople].getOffBoat(environment);
myBoat.outBoat(peopleCtrl[numberOfPeople].getName());
}
}
}
即调用人控制器的自身的getOnBoat和getOffBoat函数。
我们改了之后的getBoatOrGetShore函数实现如下:
public void getBoatOrGetShore(string name)
{
if (myBoat.getRunningState() != "waiting")
{
return;
}
int numberOfPeople = name[name.Length - 1] - '0';
if (peopleCtrl[numberOfPeople].getStatus() == "shore")
{
if (myBoat.ifHaveSeat() && myBoat.size == peopleCtrl[numberOfPeople].size)
{
myAction.getOnBoat(myBoat, peopleCtrl[numberOfPeople]);
}
}
else
{
if (myBoat.size == peopleCtrl[numberOfPeople].size)
{
myAction.getOffBoat(environment, peopleCtrl[numberOfPeople]);
myBoat.outBoat(peopleCtrl[numberOfPeople].getName());
}
}
}
可以看到,现在是场记让myAction(实例化的Action变量)来完成人的上船、下船移动,即实现了动作的分离。
船的移动的改变和人的上船下船的改变类似,在这里不用多说。
下面是新的要求:设计一个裁判类,当游戏达到结束条件时,通知场景控制器游戏结束。
我们新建一个类Judge,代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Judge : MonoBehaviour
{
// Start is called before the first frame update
public string status;
void Awake()
{
status = "playing";
}
void Start()
{
status = "playing";
}
// Update is called once per frame
void Update()
{
}
public Judge()
{
status = "playing";
}
public string getStatus()
{
return status;
}
public void setStatus(string s)
{
status = s;
}
}
里面就只是简单实现了getStatus和setStatus函数,下面则要修改场景控制器里面跟游戏结束判断相关的部分,场景控制器中的getStatus函数修改如下:
public string getStatus()
{
return myJudge.getStatus();
}
其中myJudge是Judge的实例化对象。
而在游戏状态发生变化时,也应该改变myJudge,如下:
void Update()
{
int leftDevil = 0, leftPriest = 0, rightDevil = 0, rightPriest = 0, leftShorePeople = 0;
for (int loop = 0; loop < numOfPirestOrDevil * 2; loop++)
{
if (peopleCtrl[loop].getStatus() == "shore" && peopleCtrl[loop].size == "left")
{
leftShorePeople++;
}
if (peopleCtrl[loop].getName()[0] == 'd' && peopleCtrl[loop].size == "left")
{
leftDevil++;
}
else if (peopleCtrl[loop].getName()[0] == 'd' && peopleCtrl[loop].size == "right")
{
rightDevil++;
}
else if (peopleCtrl[loop].getName()[0] == 'p' && peopleCtrl[loop].size == "left")
{
leftPriest++;
}
else
{
rightPriest++;
}
}
if ((leftDevil > leftPriest && leftPriest != 0) || (rightPriest != 0 && rightDevil > rightPriest))
{
myJudge.setStatus("lost");
}
else if (leftShorePeople == 6)
{
myJudge.setStatus("win");
}
}
这样游戏达到结束条件时,是裁判类通知场景控制器游戏结束。到这里我们的工作就基本上完成了。
游戏效果图: