3D游戏编程作业5:简易的鼠标打飞碟
简单的打飞碟游戏
设计结构
本次采用了MVC结构,在Driect.cs文件中定义了Direct,UserAction,ISceneController三个类,对于
MVC结构的作用是重要的:
代码如下;
Direct.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Direct : System.Object
{
private static Direct instance;
public ISceneController currentScene{get;set;}
public static Direct getInstance(){
if (instance==null)
instance=new Direct();
return instance;
}
}
public interface ISceneController{
}
public interface UserAction{
void getScore(int n);
void getLost(int n);
}
Direct类用于管理当前的场记类,做一些传参(用instance指明了现在的场记),还有管理一些重要功能等。
ISceneController提供了每个场记必须要有的函数,这里是空的,ISceneController主要起一个标记作用,这样在其他物体的类里面,就可以通过Direct找到当前的Controller了。
UserAction规定了用户可能会发生的事情,在复杂的情况,可以一个场记,一个Action接口。这里的GetScore是玩家获得得分的事件,Getlost是玩家丢掉分数的事件(没打到飞碟)。
FirstController.cs
场记用于处理在该场景下的一切事务,包括资源的加载,物体的调度,事件的处理等。其他的许多物体类,也都收到FirstController类的管理。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FirstController : MonoBehaviour,ISceneController,UserAction
{
// Start is called before the first frame update
public GameObject[] terrain;
public UFOController uc;
public int score;
public int lost;
public yesyes[] yes;
public GUIStyle style;
public int round;
void Start()
{
Direct.getInstance().currentScene=this;
loadResources();
Random.InitState((int)System.DateTime.Now.Ticks);
uc.StartRound();
score=0; lost=0;
round=1;
}
// Update is called once per frame
void Update()
{
if (round>=4) return;
uc.UFOUpdate();
if (uc.isOver()==1){
round++;
if (round>=4)
return;
uc.setDifficulty(round);
uc.StartRound();
}
}
void OnGUI(){
if (round>=4){
style.alignment=TextAnchor.MiddleCenter;
style.normal.textColor=Color.black;
style.fontSize=250;
if (score>lost){
GUI.Label(new Rect(0,0,Screen.width,Screen.height),"你赢了",style);
}else{
GUI.Label(new Rect(0,0,Screen.width,Screen.height),"你输了",style);
}
}
style.alignment=TextAnchor.MiddleLeft;
style.normal.textColor=Color.black;
style.fontSize=40;
GUI.Label(new Rect(0,Screen.height-100,Screen.width,100),"白 1 分 蓝 2 分 红 3 分"+" Score : "+score.ToString()+" Lost : "+lost.ToString()+" Round "+round.ToString(),style);
for(int i=1;i<=33;++i){
if (yes[i].isOn==1){
if (Time.time-yes[i].stime>=3){
yes[i].isOn=0;
continue;
}
else{
if (yes[i].color=="white") style.normal.textColor=Color.gray;
if (yes[i].color=="blue") style.normal.textColor=Color.blue;
if (yes[i].color=="red") style.normal.textColor=Color.red;
GUI.Label(new Rect(yes[i].x,yes[i].y,400,200),"YesYes",style);
}
}
}
}
public void loadResources(){
terrain=new GameObject[4];
terrain[0]=Instantiate(Resources.Load<GameObject>("Terrain"),new Vector3(0,0,0),Quaternion.identity);
terrain[1]=Instantiate(terrain[0],new Vector3(-1000,0,0),Quaternion.identity);
terrain[2]=Instantiate(terrain[0],new Vector3(-1000,0,-1000),Quaternion.identity);
terrain[3]=Instantiate(terrain[0],new Vector3(0,0,-1000),Quaternion.identity);
uc=new UFOController();
yes=new yesyes[34];
for (int i=0;i<=33;++i){
yes[i]=new yesyes();
}
}
public void getScore(int n){
score+=n;
for (int i=1;i<=33;++i){
if (yes[i].isOn==0){
yes[i].isOn=1;
yes[i].x=Input.mousePosition.x;
yes[i].y=Screen.height-Input.mousePosition.y;
yes[i].stime=Time.time;
if (n==1) yes[i].color="white";
if (n==2) yes[i].color="blue";
if (n==3) yes[i].color="red";
break;
}
}
}
public void getLost(int n){
lost+=n;
}
}
public class yesyes{
public int isOn;
public float x,y;
public float stime;
public string color;
public yesyes(){
isOn=0;
x=-200;y=-200;
stime=-100;
color="white";
}
public void over(){
isOn=0;
}
}
在FirstController里面首先在Start中确立了自己和Direct类的关系。然后加载自己的资源。
在Update函数中调用UFOController类里的函数UFOUpdate,这个是下一个文件中的类。用于管理所有的UFO,减轻场记的代码负担和混乱度。在Update中还判断了游戏结束的条件。因为游戏分为3个round(关卡),随着round的提高,游戏的难度会变高。在Update函数中要处理这些变化。
FirstController还实现了接口UserAction的函数定义,获取或者失去得分。
说说下面这个yesyes类
作用:在鼠标点击飞碟后,我希望屏幕上能显示出一个与飞碟同颜色的“yesyes”字样来提醒打到了。
参数:记录了每一个yesyes字样的应有颜色,位置,以及是否应该被显示,开始显示的时间是什么。显示时间超过三秒,就不再显示了。这些都在OnGui函数中进行了判断。
效果:
打掉的飞碟都变成了yesyes
斯卡文腐蚀+1
UFOController.cs
负责管理UFO的生成,移动,销毁,等所有事物。在此文件中还定义了有关UFO的其他类,记录UFO的信息或者管理UFO的点击事件。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class UFOController :MonoBehaviour
{
public GameObject white,blue,red;
public UFO[] ufos;
public int diff;
public float stime;
private float lastCreateTime;
public void UFOUpdate(){
int number=getNumber();
if (Time.time-stime>=9.5f){
}else{
if (Time.time-lastCreateTime>=1){
lastCreateTime=Time.time;
for (int i=1;i<=diff;++i){
CreateUFO();
}
}
}
for(int i=1;i<=33;++i){
if (ufos[i].obj!=null){
ufos[i].obj.transform.position=Vector3.MoveTowards(ufos[i].obj.transform.position,ufos[i].target,ufos[i].speed);
if (ufos[i].obj.transform.position==ufos[i].target || ufos[i].obj.transform.position.y<=4.5){
ufos[i].obj.GetComponent<UFOClass>().getOut();
number--;
}
}
}
}
public void StartRound(){
stime=Time.time;
}
public int isOver(){
if (getNumber()==0 && Time.time-stime>=10){
return 1;
}
return 0;
}
public UFOController(){
white=Resources.Load<GameObject>("whiteUFO");
blue=Resources.Load<GameObject>("blueUFO");
red=Resources.Load<GameObject>("redUFO");
ufos=new UFO[34];
for (int i=0;i<34;++i){
ufos[i]=new UFO();
}
diff=1;
stime=-100;
lastCreateTime=-100;
}
public void setDifficulty(int d){
diff=d;
}
public int getNumber(){
int ans=0;
for (int i=1;i<=33;++i){
if (ufos[i].obj!=null)
ans++;
}
return ans;
}
public void CreateUFO(){
UFO u=new UFO();
for (int i=1;i<=33;++i){
if (ufos[i].obj==null){
u=ufos[i];
break;
}
}
u.init(diff);
int kind;
kind=Random.Range(0,100);
if (kind<=33){
u.obj=Instantiate(white,u.pos,Quaternion.identity);
u.obj.name="white";
}
else if (kind<=66){
u.obj=Instantiate(blue,u.pos,Quaternion.identity);
u.obj.name="blue";
}
else{
u.obj=Instantiate(red,u.pos,Quaternion.identity);
u.obj.name="red";
}
u.obj.AddComponent(typeof(UFOClass));
}
}
public class UFO {
public float speed;
public Vector3 target;
public GameObject obj;
public Vector3 pos;
public UFO(){
}
public void init(int diff){
Object.Destroy(obj);
float y=Random.Range(60,120f);
float x=150;
if (Random.Range(0,2)==0){
x=-x;
}
pos=new Vector3(x,y,80);
x=-x;
y=Random.Range(0f,70f);
target=new Vector3(x,y,80);
speed=Random.Range(0.04f,0.06f);
speed=speed*diff;
}
}
public class UFOClass:MonoBehaviour{
private UserAction action;
public UFOClass(){
action=Direct.getInstance().currentScene as UserAction;
}
void OnMouseDown(){
string s=gameObject.name;
if (s=="white"){
action.getScore(1);
}
if (s=="blue"){
action.getScore(2);
}
if (s=="red"){
action.getScore(3);
}
Destroy(gameObject);
}
public void getOut(){
string s=gameObject.name;
if (s=="white"){
action.getLost(1);
}
if (s=="blue"){
action.getLost(2);
}
if (s=="red"){
action.getLost(3);
}
Destroy(gameObject);
}
}
按照我设计的算法,游戏中最多可能出现33个UFO,所以用一个33的数组就能存放下全部的内容。(不想用链表)
public UFO[] ufos;
CreateUFO用于创建UFO,根据难度值,对UFO的速度进行调整。
StartRound用于重新开始一个回合
SetDifficulty用于设置难度,一般用在每个回合开始前。
getNumber统计当前UFO的个数。
isOver判断当前回合有没有结束。如果飞碟数为0,并且已经开始了一段时间(防止刚开始就结束),那么此回合就结束了。将有FirstController开启下一回合。
UFOUpdate在FirstController中的Update中被调用,两者的调用是同步进行的。UFOUpdate主要用于管理飞碟的移动,判断创建新飞碟的条件,并且判断飞碟出界或者到达目的地的条件,是这个类的核心。
下面的UFO和UFOClass两个类:
UFO类是为了记录每一个飞碟的信息,比如初始位置,速度,目的地等,还有一个obj作为指针指向飞碟的GameObject。内部inti函数会根据当前的难度进行重新构造,完成了资源的重新利用。
UFOClass主要用于AddComponent函数,给飞碟的GameObject加入组件。该组件用于处理点击事件。里面的action变量是利用MVC结构的特性,从而完成了对场记中玩家事件函数的调用。
代码部分至此介绍完毕。
成果
项目链接
项目使用方法
下载Asserts.zip解压,用我的Asserts覆盖你项目的Asserts。刷新你的项目,双击UFOScene,点击运行。
推荐阅读