标题1:前言
主要是由于即将大学毕业,但是感觉什么都没学,又苦于找工作,所以想要尝试一下用之前学的u3D知识,编写一些简单的一个小恐龙游戏。很久之前根据书籍《游戏设计,原型与开发 基于untiy与C#从构思到实现》----jeremy Gibson Bond 实现过一些简单的实例,例如接苹果,弹弓(类似愤怒的小鸟),太空射击以及地牢游戏等。但是由于大学有时候比较忙,感觉有点生疏之类的,所以趁着暑假,写一些东西练习一下。
首先是谷歌小恐龙这个游戏,是谷歌浏览器在用户断网的情况下,自带的一款小游戏,基本内容就是一直恐龙在地面上跳动,然后躲避迎面而来的仙人掌和翼龙。游戏比较简单易懂,用来练习耶很不错。
但是直接实现这个游戏也有点过于简单,因此想要在这个游戏的基础上增加一些自己的内容,丰富一下,顺便也强化一下代码设计能力。
谷歌小恐龙游戏:谷歌浏览器内输入chrome://dino
基础设计
地板设计:
首先是设计地面移动的效果,一般来说这样的游戏基本是使用相对运动的模式,因此只需要地面向前移动就可以看起来像是小恐龙在前面跑动一样。为了连接的连贯性准备两个地面对象,利用卷轴进行交替移动。
上面这个是地面的对象实体
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class groundMove : MonoBehaviour
{
[Header("Set in Inspector")]
public float speed = 5f;
[Header("Set Dynamically")]
private Rigidbody rid;
// Start is called before the first frame update
void Start()
{
rid = GetComponent<Rigidbody>();//初始化刚体
}// Update is called once per frame
void Update()
{
Vector3 vel=Vector3.zero;//普通移动
vel = Vector3.left;
rid.velocity = vel*speed;
Vector3 pos=gameObject.transform.position;
if (pos.x <= 10.8f)
{
pos.x = 39.2f;
gameObject.transform.position = pos;
}
}
}
以上是地面移动代码,根据摄像机的坐标以及大小进行交替移动 ,一共两个地面对象。
仙人掌设计:
仙人掌的设计也是一样,因为恐龙并不是真正的移动,因此仙人掌的逻辑跟地面差不多,可以直接给他赋予一个向左的速度,超出屏幕就消失,然后再拿多几个仙人掌的对象
以上为使用的三个个仙人掌图像
这三个实体随机出现,仙人掌的代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class CactusMove : MonoBehaviour
{
[Header("Set inInspector")]
public float speed = 5f;
public Vector3 summonPos;//表示生成坐标
[Header("Set Dynamically")]
private Rigidbody rig;
// Start is called before the first frame update
void Start()
{
rig = GetComponent<Rigidbody>();//初始化刚体
summonPos = new Vector3(39, 3.8f, 0);//生成坐标初始化
gameObject.transform.position = summonPos;
}// Update is called once per frame
void Update()
{
Vector3 pos =gameObject.transform.position;
Vector3 vel = Vector3.left;
rig.velocity = vel * speed;
if (pos.x <= 18)
{
Destroy(gameObject);
}//超出范围销毁
}
}
翼龙设计:
游戏里面除了仙人掌这一种障碍物,还有一种叫做翼龙的障碍物,但是其实在游戏里面这个翼龙,他所拥有的逻辑其实跟仙人掌一摸一样,因此我觉得再搞一个对象再加一摸一样的脚本没什么意思,可以自己写一个。根据实例里面写过的太空设计,有一种会朝玩家冲过来的敌人,可以参考这一个,设计的翼龙会在屏幕上方随机生成,然后记录玩家当前位置(不是瞬时位置)并朝玩家冲过去。这也是比较简单的一种设计思路。那就这样写吧。
以上是翼龙的图像
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class flyMove : MonoBehaviour
{
[Header("Set in Inspector")]
public GameObject dino;//恐龙对象
public float speed;
public Vector3 dinoPos;
public Vector3 vel;
public Vector3 summonPos;
public int facing = 1;//生成在前面还是后面
private Rigidbody rig;//刚体对象
private bool isTrack;// Start is called before the first frame update
void Start()
{
dino = GameObject.Find("dino");
dinoPos = dino.transform.position;
float x = Random.Range(17, 31);//随机一个x轴坐标
summonPos = new Vector3(x, 10f, 0);
gameObject.transform.position = summonPos;
if (summonPos.x - dinoPos.x<=0)//生成在后面
{
facing = -1;
}
if (facing == -1) {//假设为后方
gameObject.transform.rotation = Quaternion.Euler(0, 180f, 0);
}//找到游戏中的恐龙对象
rig = GetComponent<Rigidbody>();//获取刚体
isTrack = false;
speed = 5f;
}// Update is called once per frame
void Update()
{
Vector3 pos =gameObject.transform.position;
if (pos.x <= 18 || pos.x >= 32)
{
Destroy(gameObject);
}//超出范围销毁
if (dino != null&&isTrack==false)//假如不存在恐龙对象
{
vel =dinoPos-gameObject.transform.position;//获取速度方向
vel.Normalize();
rig.velocity = vel*speed;
isTrack = true;
}
}
}
翼龙会根据玩家的位置转向,因为只是普通的2D模型,因此只需要绕Y轴转动180度即可。
小恐龙的设计:
小恐龙是游戏里面的主角,但是再谷歌自带的游戏里,小恐龙只可以原地跳动,这个逻辑只需要再检测按键之后,让小恐龙的RIG.velocity获得一个向上的速度即可,因此我决定再次自己写一个,根据获取的KeyCode,可以使得其进行左右移动,同时给予一个冲刺效果。
设计的时候出现了一点问题,本来的思路就是获取到Keycode之后基于对应的速度,但是由于限制有限,一旦按下左右那么小恐龙就会获得一个持续的速度,解决方法是每一次按键后速度都先设置为0。然后就是通过判断是否正在跳跃,限制小恐龙只可以再地面上才可以跳跃。最后是冲刺效果,这一个首先设置一个cd,然后cd好了之后根据是否按下Space键再根据左右之前的输入,进行两格的位移。为了搞一下那种“我刚刚冲刺了”的效果,添加了几个影子,往冲刺的方向不断递增。
这就是小恐龙的基本设计了。
上面是小恐龙和影子的对象,其实小恐龙和上面的翼龙是有一个两帧的动画的,但是我不会录视频和上传gif那就这样了。
以下为小恐龙的基本代码(未添加碰撞),我想等到游戏完善之后再添加,给敌人加上enemy标签,这应该不是什么难事。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Dino : MonoBehaviour
{[Header("Set in Inspector")]
public float speed = 5f;
public Vector3 vel = Vector3.zero;
public float FastMoveTime = 2f;//设定一个冲刺功能
public float LastFMTime;
public GameObject shadow;//影子对象
public GameObject smoke;//烟雾对象
[Header("Set Dynamically")]
public int dirHeld = -1;//这是跟方向有关的标志
private Rigidbody rid;
private Vector3[] direction = new Vector3[]{
Vector3.right,Vector3.up,Vector3.left,Vector3.down
};
private KeyCode[] keys = new KeyCode[] {
KeyCode.D,KeyCode.W,KeyCode.A,KeyCode.S
};
// Start is called before the first frame update
void Start()
{
//初始化刚体
FastMoveTime = 1f;
rid = GetComponent<Rigidbody>();
LastFMTime = Time.time;
}// Update is called once per frame
void Update()
{
Vector3 pos =gameObject.transform.position;
if (dirHeld==0||dirHeld==2)
{
vel = Vector3.down;
rid.velocity = vel;
}//防止左右移动出现一直移动问题
if (pos.y <= 4f)//当低于地面范围时
{
pos.y = 4f;
gameObject.transform.position = pos;
}//用于校准
if (pos.x <= 17.5f)
{
pos.x = 18f;
gameObject.transform.position = pos;
return;
}
if (pos.x >= 32.5f)
{
pos.x = 32f;
gameObject.transform.position = pos;
return;
}//分别是三个边框校准
dirHeld = -1;
for(int i = 0; i < 4; i++)
{
if (Input.GetKey(keys[i]))
{
dirHeld = i;
}
}
vel = Vector3.zero;
if (dirHeld > -1)
{
if (dirHeld == 1 && pos.y > 4f)
{
return;
}//只可以再地面上跳跃
vel = direction[dirHeld];
rid.velocity = vel * speed;
}
if (Time.time - LastFMTime >= FastMoveTime)//可以冲刺了
{
//重设时间
if (Input.GetKey(KeyCode.Space) && (dirHeld == 0 || dirHeld == 2))//如果按下了空格
{
LastFMTime = Time.time;
int facingNum = 0;
if (dirHeld == 0)//往右边
{
facingNum = 1;
}
else if (dirHeld == 2)//往左边
{
facingNum = -1;
}
Vector3 tem = gameObject.transform.position;
tem.x += facingNum*2f;
for (int i = 0; i < 5; i++)
{
gameObject.transform.position = tem;
GameObject sm = Instantiate(shadow);
tem.x += -(facingNum*(float)i*0.08f);
sm.transform.position = tem;
}
}
}
}
}
敌人生成器:
可以看到,在上面的仙人掌和翼龙对象,并没有对应的实例化生成代码。因为我在主摄像机里面添加了一个敌人生成的脚本,这个脚本基本思路就是按着时间间隔生成敌人,随机生成的,有可能是仙人掌也可能是翼龙也可能是后面添加的一些东西,下面的代码里是添加了一些东西之后的,但是这篇文章只介绍刚开始的。除了这一个,还设计一个波次的逻辑,每生成20个敌人为一波,然后没过一波就减少生成间隔直到最大值,同时后面设计为每隔几波就出一个boss,敌人对象放在GameObject的一个速度里,然后随机列入一个大小为十的数组,这样可以增加随机性。
对于其生成就差不多这样:
下面是代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class CactusSummoner : MonoBehaviour
{
[Header("Set in Inspector")]
public GameObject[] cactus;//仙人掌的保留数组
public GameObject[] Emeny;//敌人生成的数组
public float timeSum;//时间间隔
public float timeNow;
public float timeSLimit = 1f;
public int numOfSummon;
public int wave;
public int sizeOfEmeny;//需要在引擎处初始化
public GameObject[] Boss;
private bool isBoss;
// Start is called before the first frame update
void Start()
{
timeNow = Time.time;//记录目前时间
timeSum = 1.5f;
numOfSummon = 0;
wave = 1;
sizeOfEmeny = 6;
isBoss = false;
}// Update is called once per frame
void Update()
{
if (wave % 5 == 0)
{
if (isBoss == false)//场上不存在boss
{
Instantiate(Boss[0]);//此处为生成boss
isBoss = true;//使得boss为真}
GameObject bs = GameObject.Find("ks1(Clone)");
if (bs == null)
{//boss消失
isBoss = false;
numOfSummon = 0;
wave++;
}
}
else
{
if (numOfSummon >= 20)//一波为20
{
for (int i = 0; i < 10; i++)
{
int ge = Random.Range(0, sizeOfEmeny);
cactus[i] = Emeny[ge];//在敌人数组里面抽取
}
numOfSummon = 0;
if (timeSum <= timeSLimit)
{
timeSum = timeSLimit;
}
else
{
timeSum -= 0.2f;
}
wave++;
}
Vector3 SumPos = new Vector3(34, 3.8f, 0);//记录生成坐标
if (Time.time - timeNow >= timeSum)//时间间隔足够
{
timeNow = Time.time;
int ranNum = Random.Range(0, 10);//随机抽取仙人掌
Instantiate(cactus[ranNum]);//生成仙人掌
numOfSummon += 1;//增加一次计数
}
}
}
}
上面那个跟boss有关的代码还是测试代码,可以无视,后面会将“ks1”改为检测boss的tag的方式。这就是游戏的基本设计了。
基本效果图:
结语
这一个基础设计大概就是这样,本人也是正在unity的入门阶段,代码写得很乱请多多谅解,这个文章主要是记录一下练习。谢谢大家,我还会继续增加一些新内容。