前言
是的,我回来了,鸽了一大段时间,抽空花两天设计了一些新东西。虽然最近被一堆师兄老师pua,导致徒增了不少焦虑。但是感觉时不时回来写一下这个谷歌小恐龙也起到了舒缓情绪的作用。而且突然有一个不错的思路。在之前我们设计的BOSS都是一大个的boss,来自泰拉瑞亚这款游戏。本来想着延续下去,去把泰拉瑞亚新三王的设计偷过来结果发现这玩意在wiki上面没有动画。导致我不得不想别的办法。于是,这一次的设计就参考了泰拉瑞亚里面的其他设计----入侵事件。
入侵事件
本次设计虽然也是把这个模块放在了BOSS这里,但是这个boss并非一个个体,而是一个入侵事件。我要参考的,是来自泰拉瑞亚困难模式的入侵事件--海盗入侵。
原本游戏中入侵事件就是一堆怪从东南西北发动袭击,然后会有一个大boss设计。考虑到很久没有设计过小怪,那么就用这个练练手吧!
话不多说,直接开始本次练习
固定方位袭击
固定方位袭击的敌人会从左右两边,以及天空袭击下来。这里我设计了三个敌人
鹦鹉,鲨鱼以及水母
由于鹦鹉的设计与本来就存在的翼龙很相似,因此直接使用其脚本:
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;
}
}
}
然后就是鲨鱼和水母,两者的设计思路基本一致,靠近主角然后发动突然袭击!
鲨鱼的袭击,是检测主角的瞬时位置,在靠近主角时,跃出水面:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class sharkMove : MonoBehaviour
{
[Header("设置")]
public GameObject dino;
public Vector3 dinoPos//属性主角坐标
{
get
{
return dino.transform.position;
}
set
{
value = dino.transform.position;
}
}
public Vector3 Pos//属性自身坐标
{
set
{
gameObject.transform.position = value;
}
get
{
return gameObject.transform.position;
}
}
public Vector3[] summonPos = new Vector3[]//初始位置生成
{
new Vector3(30f,3f,0),new Vector3(20f,3f,0)
};
private Rigidbody rig;
private Transform trs;
private Vector3 vel;
private bool isJump;
// Start is called before the first frame update
void Start()
{
dino = GameObject.Find("dino");//获取恐龙的实例化
rig = gameObject.GetComponent<Rigidbody>();//获取刚体实例化
trs = gameObject.GetComponent<Transform>();//获取transform实例化
int num = Random.Range(0, 2);
Pos = summonPos[num];//获取生成位置
if (num == 1)//根据生成我位置获取速度
{
rig.velocity = Vector3.right * 5f;
vel = rig.velocity;
}
else
{
rig.velocity = Vector3.left * 5f;
vel = rig.velocity;
}
isJump = false;
}
// Update is called once per frame
void Update()
{
if (Pos.x <= 18f || Pos.x >= 32f)
{
Destroy(gameObject);
}
if (Mathf.Abs(dinoPos.x - Pos.x) <= 3f&&isJump==false)
{
isJump = true;
rig.velocity = Vector3.zero;
rig.velocity = Vector3.up * 8f + vel;
}
}
}
可以发现,这次我使用了一个与众不同的变量定义----属性
一种C#中的特殊语法,用于传入变量时可以进行修饰以及特殊修改,是一种十分便捷的变量定义方式,好像java也有类似的语法。 属性这个以后还会经常使用,说实话,使用属性也可以让代码更加好看。
然后是水母,水母在出生时就检测主角位置,然后游到那个位置飞起来袭击主角。同时会改变贴图,起飞时贴图修改为放电的水母。以下为代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class jellyFinsh : MonoBehaviour
{
[Header("设置")]
public Vector3 dinoPos;
public GameObject dino;
public Vector3 vel;
public Vector3[] SummonPos = new Vector3[]
{
new Vector3(30f,3f,0),new Vector3(19.5f,3f,0)
};
private Rigidbody rig;
private SpriteRenderer spr;
public Sprite[] sp;
private bool isOn;
public Vector3 Pos
{
set
{
value = gameObject.transform.position;
}
get
{
return gameObject.transform.position;
}
}
// Start is called before the first frame update
void Start()
{
int num = Random.Range(0, 2);
gameObject.transform.position = SummonPos[num];//随机生成在两边
dino = GameObject.Find("dino");
dinoPos = dino.transform.position;//获得初始值
isOn = false;
rig = gameObject.GetComponent<Rigidbody>();
spr = gameObject.GetComponent<SpriteRenderer>();//获取组件
if (num == 1)//根据生成我位置获取速度
{
vel = Vector3.right*5f;
}
else
{
vel = Vector3.left*5f;
}
rig.velocity = vel;//获取速度
}// Update is called once per frame
void Update()
{
if (Pos.y>=10f)
{
Destroy(gameObject);
}
if ((Pos.x - dinoPos.x) <= 0.5f&&isOn==false)
{
rig.velocity = Vector3.zero;
spr.sprite = sp[1];//修改贴图
rig.velocity = Vector3.up * 10f;
isOn=true;
}
}
}
以上就是固定方位袭击的对象设计。
海盗
海盗会从空中跳下来对玩家发动袭击,我对此设计了4种敌人
持刀海盗:
持刀海盗式一种普通的敌人,在地面上会朝玩家接近,每隔一段时间就会跳起来,非常简单的设计
以下为代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class priateMove : MonoBehaviour
{
[Header("设置")]
public GameObject dino;
public Vector3 dinoPos//属性主角坐标
{
get
{
return dino.transform.position;
}
set
{
value = dino.transform.position;
}
}
public Vector3 Pos//属性自身坐标
{
set
{
gameObject.transform.position = value;
}
get
{
return gameObject.transform.position;
}
}
public Vector3 vel;//速度
public float health;//生命
private Rigidbody rig;
public float timeNow;
public float timePast;
// Start is called before the first frame update
void Start()
{
rig = gameObject.GetComponent<Rigidbody>();
health = 2f;
dino = GameObject.Find("dino");
vel = Vector3.zero;
timeNow = Time.time;
timePast = 2f;
}// Update is called once per frame
void Update()
{
if (health <= 0)
{
Destroy(gameObject);
}
if (Pos.y == 3.75f)//在陆地上
{
rig.velocity = Vector3.zero;
if (Pos.x - dinoPos.x <= 0)//根据玩家在哪使用
{
rig.velocity = Vector3.right*2f;
}
else
{
rig.velocity = Vector3.left*2f;
}
}
if(Time.time - timeNow >= timePast)
{
Vector3 tem = Pos;
tem.y = 3.8f;
Pos = tem;
timeNow = Time.time;//重置实践
vel = rig.velocity;
vel.y = 5f;
rig.velocity = vel;
}
}
void OnTriggerEnter(Collider collider)//触发器
{
if (collider.tag == "projectile")
{
GameObject tem = collider.gameObject;//获取游戏对象
smilePro sp = tem.GetComponent<smilePro>();
health -= sp.Damage;
}
}
}
海盗弩手与船长
海盗弩手会朝玩家射击弩箭,没射击一次就会根据随机数朝玩家翻滚。 海盗船长与海盗弩手使用同一个脚本,但是发射的子弹不相同,弹道也不会进行上调。
以下为代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class priateShot : MonoBehaviour
{
[Header("设置")]
public GameObject arrow;
public GameObject dino;
public Vector3 dinoPos
{
get
{
return dino.transform.position;
}
set
{
dino.transform.position = value;
}
}
public Vector3 Pos
{
get
{
return gameObject.transform.position;
}
set
{
gameObject.transform.position = value;
}
}
public Rigidbody rig;
public Vector3 vel;
private Transform trs;
private float timeNow;
public float timePast = 1.5f;
private float health;
private float facing;
private bool isRoll;
private bool canTrack;
public bool isBoss = false;
// Start is called before the first frame update
void Start()
{
dino = GameObject.Find("dino");
rig = gameObject.GetComponent<Rigidbody>();
vel = Vector3.zero;
trs = gameObject.GetComponent<Transform>();
timeNow = Time.time;
health = 2f;
facing = 0;
isRoll = false;
canTrack = false;
}// Update is called once per frame
void Update()
{
if (health <= 0)
{
Destroy(gameObject);
}
if (Pos.x > dinoPos.x)
{
facing = 1;
}
else
{
facing = -1;
}//更改朝向表示翻滚
if (Pos.y == 3.75f)//在陆地上
{
isRoll = false;
if(canTrack == true)
{
FacingPlayer();//调用看玩家函数
canTrack = false;//只调用一次方式防止抽搐
}
rig.velocity = Vector3.zero;//重置速度
if (Time.time - timeNow >= timePast)
{
timeNow = Time.time;
vel = dinoPos - Pos;//获取一个速度
vel.Normalize(); vel *= 10f;
if (!isBoss)
{
vel.y += 1f;
}
GameObject ar = Instantiate(arrow);//实例化弩箭
ar.transform.position = Pos;
Rigidbody arig = ar.GetComponent<Rigidbody>();//获取弩箭的刚体实例化
arig.velocity = vel;
int num = Random.Range(0, 10);
if (num >= 5)//随机跳跃
{
//翻滚
rig.velocity = Vector3.up * 5f + Vector3.left * facing * 5f;
isRoll = true;
canTrack = true;//下一次到地面就可以重置视角
return;
}
}
}
if (isRoll == true)
{
//print("x");
trs.Rotate(Vector3.forward * 360f * Time.deltaTime);//旋转
}
}
void FacingPlayer()
{
if(Pos.x>dinoPos.x)
{
gameObject.transform.rotation = Quaternion.Euler(0, 0, 0);//旋转180度
}
else
{
gameObject.transform.rotation = Quaternion.Euler(0, 180, 0);//旋转180度
}
}
void OnTriggerEnter(Collider collider)//触发器
{
if (collider.tag == "projectile")
{
GameObject tem = collider.gameObject;//获取游戏对象
smilePro sp = tem.GetComponent<smilePro>();
health -= sp.Damage;
}
}
}
大炮
大炮是一种固定射击敌人,无法被子弹损坏,存活时间一到就会直接被摧毁,期间每隔两秒就会发射两种不同弹道的炮弹,并且会根据炮弹的弹道修改贴图,以下为代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class shoter : MonoBehaviour
{
[Header("设置")]
public GameObject dino;
private Rigidbody rig;
public Sprite[] sp;
public GameObject Projectile;
public Vector3 Pos
{
get
{
return gameObject.transform.position;
}
set
{
gameObject.transform.position = value;
}
}//关于位置信息的属性
public Vector3 dinoPos
{
get
{
return dino.transform.position;
}
set
{
dino.transform.position = value;
}
}
private int roll;
public float timeNow;
public float timePast;
public float summonTime;
public float liveTime;
private SpriteRenderer spr;
private Vector3 vel;
// Start is called before the first frame update
void Start()
{
dino = GameObject.Find("dino");
rig = gameObject.GetComponent<Rigidbody>();
spr = gameObject.GetComponent<SpriteRenderer>();
roll = 0;
timeNow = Time.time;
summonTime = Time.time;
liveTime = 10f;
timePast = 2f;
}// Update is called once per frame
void Update()
{
if(Time.time - summonTime >= liveTime)
{
Destroy(gameObject);
}//时间到了自动销毁
spr.sprite = sp[roll];//更新贴图
if (Pos.y == 3.75)
{
if (Time.time - timeNow >= timePast)
{
timeNow = Time.time;
switch (roll)
{
case 0:
vel = Vector3.right * 10f;
break;
case 1:
vel = Vector3.right * 5f + Vector3.up * 6f;
break;
default: break;
}
roll = Random.Range(0, 2);//重新摇号
GameObject pro = Instantiate(Projectile);
pro.transform.position = Pos;
Rigidbody prig = pro.GetComponent<Rigidbody>();
if (dinoPos.x > Pos.x)//根据方向修改弹道
{
vel.x *= 1;
}
else
{
vel.x *= -1;
}
prig.velocity = vel;
}
}
}
}
这样,关于海盗的部分就设计完成了!
通用脚本
本次设计还考虑了一些,可能多次使用的方法,我将其整合为不同的脚本,这次设计了保持在地面的脚本,面对玩家的脚本,一直注视玩家的脚本。
同时,面对玩家以及注视玩家的脚本可以设置是否每一帧执行,例如子弹的只需要在初始的第一帧Start()中执行一次即可。
保持在地面脚本:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class BoundTrack : MonoBehaviour
{
[Header("设置")]
public Vector3 Pos;
public float Bound = 3.75f;
// Start is called before the first frame update
void Start()
{
Pos = Vector3.zero;
}// Update is called once per frame
void Update()
{
Pos = gameObject.transform.position;
if (Pos.y <= Bound)
{
Pos.y = Bound;
}
gameObject.transform.position = Pos;
}
}
面向玩家脚本:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class FacingPlayer : MonoBehaviour
{
[Header("设置")]
private float rat = 0f;
private int facing = 1;//表示的是对象处于主角的前后方
private int lastfacing = 1;
public GameObject Dino;
public Vector3 dinoPos;
public bool isUpdate = true;
// Start is called before the first frame update
void Start()
{
Dino = GameObject.Find("dino");
dinoPos = Dino.transform.position;
facingPlayer();
}// Update is called once per frame
void Update()
{
if (isUpdate)
{
facingPlayer();
}
else
{
return;
}
}
public void facingPlayer()//面向玩家
{
Vector3 pos = gameObject.transform.position;//获取自己位置
dinoPos = Dino.transform.position;
if (pos.x - dinoPos.x <= 0)//表示在后面
{
facing = -1;//更改朝向
}
else if (pos.x - dinoPos.x > 0)//表示在前面
{
facing = 1;//更改朝向
}if (facing != lastfacing)//两个朝向不一致
{
//print("trans aug");测试是否转角
rat += lastfacing * 180f;
gameObject.transform.rotation = Quaternion.Euler(0, rat, 0);//旋转180度
lastfacing = facing;//更改上一次朝向
}
}
}
注视玩家脚本:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class PlayerTrack : MonoBehaviour
{
[Header("设置")]
public GameObject dino;
public Vector3 dinoPos;
public bool isUpdate;
// Start is called before the first frame update
void Start()
{
dino = GameObject.Find("dino");
dinoPos = dino.transform.position;
TrackPlayer();
}// Update is called once per frame
void Update()
{
if (isUpdate)
{
TrackPlayer();
}
else
{
return;
}
}
public void TrackPlayer()//眼睛紧跟着玩家
{
Vector3 eyePos = gameObject.transform.position;//获取眼睛当前位置
dinoPos = dino.transform.position;//获取玩家坐标
float length = (dinoPos - eyePos).magnitude;//获得长度
float Ang = Mathf.Abs((dinoPos.y - eyePos.y) / length);//获得一个sin值
float A2 = (Mathf.Asin(Ang) * Mathf.Rad2Deg);//反sin函数加上弧度制获得角度
if (eyePos.y <= dinoPos.y)//假如在下方
{
A2 += 90f;//加上九十度
}
//在左边
//计算确切角度;
if (eyePos.y >= dinoPos.y)//在上方需要修改
{
A2 = 90f - A2;
}
if (eyePos.x <= dinoPos.x)//在左边
{
gameObject.transform.rotation = Quaternion.Euler(0, 0, A2);
}
else
{
gameObject.transform.rotation = Quaternion.Euler(0, 0, -A2);
}}
}
海盗船
海盗船的设计很简单,就是会一直在右上角盘旋,然后三个大炮会不断以随机的速度发射炮弹。
以下为脚本:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class ship : MonoBehaviour
{
[Header("设置")]
public GameObject dino;
public Vector3 dinoPos
{
get
{
return dino.transform.position;
}
set
{
dino.transform.position = value;
}
}
public Vector3 Pos
{
get
{
return gameObject.transform.position;
}
set
{
gameObject.transform.position = value;
}
}
private float timeNow;
private float timePast;
private Rigidbody rig;
private Vector3 summonPos;
public bool isStand;
private Vector3 standPos;
// Start is called before the first frame update
void Start()
{
dino = GameObject.Find("dino");
rig = gameObject.GetComponent<Rigidbody>();
summonPos = new Vector3(34.12f, 9f, 0);
standPos = new Vector3(28.69f, 9f, 0);
timeNow = Time.time;
timePast = 1f;
isStand = false;
Pos = summonPos;
rig.velocity = Vector3.left * 3f;
}// Update is called once per frame
void Update()
{
if ((Pos - standPos).magnitude <= 0.2f&&isStand == false)//检测是否到达位置
{
isStand = true;//已经到达预定位置
rig.velocity = Vector3.down*2f;//重置速度
timeNow = Time.time;//重置时间
}
if (isStand == true)
{
if(Time.time - timeNow >= timePast)
{
timeNow = Time.time;
rig.velocity *= -1f;
}
}
}
}
船上的大炮是空对象绑定船为父对象,用于发射炮弹 ,以下为脚本:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class shipshoter : MonoBehaviour
{
[Header("设置")]
public GameObject projectlie;
private float timeNow;
public float timePast;
// Start is called before the first frame update
void Start()
{
timePast = 2f;
timeNow = Time.time;
}// Update is called once per frame
void Update()
{
if(Time.time - timeNow >= timePast)
{
timeNow = Time.time;
GameObject pro = Instantiate(projectlie);
Rigidbody prig = pro.GetComponent<Rigidbody>();
pro.transform.position = gameObject.transform.position;
prig.velocity = Vector3.left * (float)Random.Range(3, 8) + Vector3.up * (float)Random.Range(1, 3);
}
}
}
控制台
根据波次释放海盗以及袭击生物,生成海盗船,以及更新GUI。以下为脚本:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;public class priateControler : MonoBehaviour
{
[Header("设置")]
public GameObject[] priate;//海盗
public GameObject[] seaCreature;//海洋生物
private int wave;//波次
public GameObject ship;//海盗船的游戏对象
public Vector3[] SummonPos = new Vector3[]
{
new Vector3(21.26f,9.5f,0),new Vector3(25f,9.5f,0),new Vector3(29f,9.5f,0)
};
public float timeNow;
public float timePast;//波次时间
[Header("UI设置")]
[Header("UI设置")]
public TextMeshProUGUI bossLife;//用于实例化UI对象
public GameObject LifeCount;//映射到血量UI;
// Start is called before the first frame update
void Start()
{
timeNow = Time.time;
timePast = 20f;//十秒
Instantiate(ship);//生成海盗船
gameObject.transform.position = new Vector3(25f, 6f, 0);
wave = 0;
LifeCount = GameObject.Find("bossLife");
bossLife = LifeCount.GetComponent<TextMeshProUGUI>();
bossLife.text = "PrivateWave:"+wave+"/6";
}// Update is called once per frame
void Update()
{
if (wave > 6)//十波之后销毁游戏对象
{
string tem = " ";
bossLife.text = tem;
GameObject sh = GameObject.Find("ship(Clone)");
if (sh)
{
Destroy(sh);
}
Destroy(gameObject);
}
else
{
if(Time.time - timeNow >= timePast)//波次时间到了
{
timeNow = Time.time;
wave++;
UpDateUI();//更新UI;
for (int i = 0; i < 3; i++)
{
GameObject pr = Instantiate(priate[Random.Range(0, 4)]);//生成可以放置的海盗
pr.transform.position = SummonPos[i];//分别生成在三个预设位置
Instantiate(seaCreature[Random.Range(0, 3)]);//生成海洋生物
}
}
}
}
void UpDateUI()
{
string tem = "PrivateWave:" + wave + "/6";
bossLife.text = tem;
}
}
以上就是本次设计所有的脚本啦!以下为设计的效果图以及视频:
视频:
(审核中)
后记
好啦,这次练习就结束啦!感谢大家支持,有时间我还会继续添加新内容!