<a target=_blank href="http://weibo.com/DoubleIris">微博@卷毛的呈秀波</a>
上回我们已经建立了游戏中的基本要素,这回我们要建立一个交互界面。
作为一个塔防游戏,他的互动要素有以下
- 点击按钮
- 在空地放置炮台
简言之就是操作界面的建立。
UI界面
我们来想一下UI界面的要素
- 按下按钮之后可以创建炮台
- 能在怪物的移动轨道之外建立炮台
伪代码可以写成
if(buttonpressed == TRUE)
Tower.Render.enabled = true;
if(Tower.Render.enabled == true){
if(mousecontact.Object != path&&!=tower){
createTower(Pos(mouse.position));
Tower.Rander.enabled = false;
}
}
前期工作
要完成这两个功能,我们需要了解几个函数
Object.Instantiate()
这个函数是不是很眼熟,没错,这就是上一篇中我们用来复制Monster 的克隆函数,具体细则参照上一篇。
这个函数用来获取鼠标按键的返回消息,参数分别对应左右中键。
这个函数用于生成GUI界面的按钮,返回按钮是否被按下。一共有六个重载,挑选自己所需要的使用就好
用来返回射线是否触碰到物体
不太懂没关系,接下来我们将会实际使用到。
首先将我们上次在场景中创建的Tower隐藏或者移除。
逻辑1
于Project-》Script下新建一个脚本,名为createtower 。
首先我们要绘制GUI按钮,在MonoBehavior中添加如下代码
void OnGUI()
{
if(GUI.Button(new Rect(0,0,64,64),"点击这里”))
Debug.Log("click");
}
将它挂载到Main Camera上
点击播放,可以看见下图
左上角出现了按钮,并且点击之后consle会输出信息“click”
这里就要了解OnGUI这个过程了,他是专门用来绘制GUI可见对象的一个函数。我们使用GUI。Button生成的按钮在这里会被调用。具体说明请戳我
而在这里我们需要做的事情是,按下按钮后,进入可以放置炮台的状态。所以我们要新建一个bool 类型的变量用来管理状态。
bool isCreate = false;//默认关闭
然后修改我们的OnGUI
void OnGUI()
{
if(GUI.Button(new Rect(0,0,64,64),"点击这里"))
isCreate = true;
if (isCreate == true)
{
GUI.Button(new Rect(Input.mousePosition.x - 32, Screen.height - Input.mousePosition.y - 32, 64, 64), "这里是模型");
Debug.Log("Click");
}
}
在这里我们又生成了一个实时跟随鼠标的GUIButton,用来告诉玩家当前处于可以创建炮台的状况= =因为我没模型所以用了String来代替。
检查一下效果
逻辑2
现在我们拥有了可以操作的按钮但是还是不能生成炮台。
生成炮台需要两个判断
- 是否处于可以创建炮台的模式下
- 鼠标点击的地方是不是path
第一个判断我们已经使用bool变量来存储了,现在要解决第二个判断。这个就要用到之前出现的两个函数Raycast和GetMBDown。
声明两个变量,Ray,和RayHit用于Raycast的调用
public Ray distance;
public RaycastHit hit;
还要声明一个gameObject类,用来挂载Tower
然后在Update中添加下列代码
void Update ()
{
distance = camera.ScreenPointToRay(Input.mousePosition);//获取从摄像机的投影平面向鼠标位置发射的射线
if (isCreate == true)//前提需要能够创建炮台
{
if (Physics.Raycast(distance, out hit))//如果射线触碰到了物体
{
if (hit.collider.tag != "path")//前提2不是怪物的路径
{
if (Input.GetMouseButtonDown(0) && hit.collider.name != "Tower")//按下左键切鼠标触碰到的碰撞体不是塔
{
Object.Instantiate(mTower, hit.collider.transform.position, Quaternion.identity);
isCreate = false;//把炮台复制过来,并且关闭可以创建的状态
}
}
}
}
}
然后运行一下
如果你和我的设置一样会发下有两个个问题
- 创建的炮塔都重复在中间,并且一个叠加在另一个上面
- 炮台下沉在地面之下
第二个问题很简单,因为我们的圆柱本来就要上升0.5个单位,所以在Instantiate (的transform后面加Vec(0,0.5,0))就能够让炮台准确的在地面上浮现了。
第一个问题好像完全不符合情理,我们已经在代码中做了判定,却依旧能够在炮塔身上建立炮台?
这个问题我们需要深究一下。
重新看我们的代码:
if (Input.GetMouseButtonDown(0) && hit.collider.name != "Tower")
我们可以看见我们所做的判断是判定射线触碰到的物体名字是否为Tower,但是实际观察一下我们的组件栏
他的名字是Tower(Clone)!!! 你是不是完全没有在意!
再看另一行代码
Object.Instantiate(mTower, hit.collider.transform.position, Quaternion.identity);
这行代码将我们的炮台复制到的地方是射线触碰到物体的位置(中心),这不符合常理,因为地面是一个物体,我们不可能将所有炮台都建立在地面中心。
关于这一点我们有两种解决方案:
- 在地图上建立一个个炮架,这样炮台就会建立在炮架上。
- 把炮台的复制位置换成射线触碰的点。
在这里,加入选用第一种方案的话,势必就要再给炮架添加一个判断脚本,太繁琐所以我们直接将hit.collider.transform.position改变成hit.point.position.
最后我们的代码长成这个样子
void Update ()
{
distance = camera.ScreenPointToRay(Input.mousePosition);
if (isCreate == true)
{
if (Physics.Raycast(distance, out hit))
{
if (hit.collider.tag != "path")
{
{ //getMouseDown(x) :x=0,left button;1,right button;2,middle button)
if (Input.GetMouseButtonDown(0) && hit.collider.name != "Tower(Clone)")
{
Object.Instantiate(mTower, hit.point + new Vector3(0f, 0.5f, 0f), Quaternion.identity);
isCreate = false;
Debug.Log(hit.point );
Debug.Log(hit.collider.name);
}
}
}
}
}
}
然后你就能看见下图
耶,我们的塔防游戏又完善了一步。
ps,笔者杂谈
最近由于各种琐碎的事情,导致上周的教程完全没有更新,今天突然有一个读者咨询我教程,感觉自己的努力也是有回报的。
于是登陆了优快云,看见还给我的博客颁发了持之以恒的徽章。顿时觉得只要你们能从笔者的简陋教程中获取一些知识,笔者再辛苦也是值得的。
我是妖哲,明天见~