学习游戏制作记录(boss的制作)
我们想制作一个会随机传送或者释放法术的boss
1.实现boss的随机传送
状态机依旧参考骷髅
修改Enemy_DeathBringer脚本:
[Header("Teleport Details")]
[SerializeField] private BoxCollider2D arena;//传送的区域
[SerializeField] private Vector2 surroundingCheckSize;//检测附近有无障碍物的半径
public float chanceToTeleport;//传送的概率
public float defaultChanceToTeleport=25;//默认传送的概率
#region States
public DeathBringerIdleState idleState;
public DeathBringerBattleState battleState;
public DeathBringerAttackState attackState;
public DeathBringerDeadState deadState;
public DeathBringerSpellCastState spellCastState;
public DeathBringerTeleportState teleportState;
#endregion
public void FindPosition()
{
FindPositionWithLimit(50); // 最多尝试50次
}
private void FindPositionWithLimit(int maxAttempts)//寻找位置
{
int attempts = 0;
while (attempts < maxAttempts)
{
// 生成随机位置
float x = Random.Range(arena.bounds.min.x + 3, arena.bounds.max.x - 3);
float y = Random.Range(arena.bounds.min.y + 3, arena.bounds.max.y - 3);
transform.position = new Vector3(x, y);
transform.position = new Vector3(transform.position.x,
transform.position.y - GroundBelow().distance + (cd.size.y / 2));
// 如果位置合适,直接返回
if (GroundBelow() && !SomethingIsAround())
{
return; // 找到合适位置,退出函数
}
attempts++;
}
}
private RaycastHit2D GroundBelow() => Physics2D.Raycast(transform.position, Vector2.down, 100, whatIsground);//离地距离和地面检测
private bool SomethingIsAround() => Physics2D.BoxCast(transform.position, surroundingCheckSize, 0, Vector2.zero, 0, whatIsground);//检测是否在地面里面
protected override void OnDrawGizmos()
{
base.OnDrawGizmos();
Gizmos.DrawLine(transform.position, new Vector3(transform.position.x, transform.position.y - GroundBelow().distance));
Gizmos.DrawWireCube(transform.position, surroundingCheckSize);
}
public bool canTeleport()//是否可以传送
{
if(Random.Range(0,100)<=chanceToTeleport)
{
chanceToTeleport = defaultChanceToTeleport;//每次传送成功恢复默认概率
return true;
}
else
return false;
}
创建DeathBringerTeleportState脚本:
public override void Enter()
{
base.Enter();
enemy.state.MakeInvicibale(true);//进入传送时无敌
}
public override void Update()
{
base.Update();
if(triggerCalled)
{
enemyStateMachine.ChangeState(enemy.battleState);//攻击结束跳转到战斗状态追击玩家
}
}
public override void Exit()
{
base.Exit();
enemy.state.MakeInvicibale(false);//退出时解除无敌
}
修改DeathBringerIdleState脚本:
public override void Update()
{
base.Update();
if(Input.GetKeyDown(KeyCode.Alpha5))//仅用于调试按下5使boss开始传送
{
enemyStateMachine.ChangeState(enemy.teleportState);
}
}
修改DeathBringerAttackState脚本:
public override void Enter()
{
base.Enter();
enemy.chanceToTeleport += 5;//每次攻击都会增加传送的概率
}
public override void Exit()
{
base.Exit();
enemy.lastAttackTime = Time.time;
}
public override void Update()
{
base.Update();
enemy.SetZeroVelocity();
if (triggerCalled)
{
if (enemy.canTeleport())//攻击结束后进行一次传送
enemy.enemyStateMachine.ChangeState(enemy.teleportState);
else//否则继续战斗
enemyStateMachine.ChangeState(enemy.battleState);
}
}
创建Enemy_DeathBringerAnimationTrigger脚本:
public class Enemy_DeathBringerAnimationTrigger : Enemy_AnimationTrigger
{
private Enemy_DeathBringer enemyDeathBringer => GetComponentInParent<Enemy_DeathBringer>();
private void ReLocate() => enemyDeathBringer.FindPosition();//进行传送,绑定在传送消失的最后一帧
private void MakeInvisible() => enemyDeathBringer.entityFX.MakeTransprent(true);//恢复血条的显示,绑定在传送出现的第一帧
private void Makevisible() => enemyDeathBringer.entityFX.MakeTransprent(false);//隐藏血条,绑定在传送消失的最后一帧
}
UI_HealthBar脚本:
private void OnEnable()//使启用和事件绑定同步进行
{
entity.onFlipped += FlipUI;
mystate.onHealthChange += UpdateHealthUI;
}
private void UpdateHealthUI()
{
slider.maxValue = mystate.GetMaxHealth();
slider.value = mystate.currentHealth;
}
private void OnDisable()
{
if(entity!= null)
{
entity.onFlipped -= FlipUI;
}
if(mystate!= null)
{
mystate.onHealthChange -= UpdateHealthUI;
}
}
2.boss的法术实体制作
创建DeathBringerSpell_Controller脚本:
[SerializeField] private Transform check;//法术攻击检查位置
[SerializeField] private Vector2 boxSize;//攻击范围
[SerializeField] private LayerMask WhatIsPlayer;//图层
private CharactorState mystats;//使用法术者的状态
public void SetUpSpell(CharactorState _mystats) => mystats = _mystats;//设置
public void AnimationTrigger()//动画触发器,在法术伤害的动画调用
{
Collider2D[] collider2Ds = Physics2D.OverlapBoxAll(check.position, boxSize, WhatIsPlayer);
foreach (var hit in collider2Ds)
{
if (hit.GetComponent<Player>() != null)
{
hit.GetComponent<Entity>().SetupKnocbackDir(transform);
mystats.DoDamage(hit.GetComponent<CharactorState>());
}
}
}
private void OnDrawGizmos()=>Gizmos.DrawWireCube(check.position, boxSize);
private void SelfDestroy() => Destroy(gameObject);//自毁,法术结束时销毁实体
3.boss施法的实现
Enemy_DeathBringer脚本:
public bool BossFigthBegun;//boss战是否开始
[Header("SpellCast Details")]
[SerializeField] private GameObject spellPrefab;//法术实体的预制体
public float lastTimeCast;//上次释放法术的时间
public int amoutsOfSpells;//法术实体的数量
public float SpellCastCooldown;//不同实体间释放的冷却
[SerializeField] private float SpellCastStateCooldown;//进入施法状态的冷却
public void CastSpell()//施法并产生实体
{
Player player = PlayerManage.instance.player;
float xOffest = 0;
if (player.rb.velocity.x != 0)
xOffest = player.facingDir * 3;//预判玩家的位置,在玩家附近生成法术实体
Vector3 spellPosition = new Vector3(player.transform.position.x + xOffest, player.transform.position.y + 1.5f);
GameObject newSpell=Instantiate(spellPrefab,spellPosition, Quaternion.identity);//生成
newSpell.GetComponent<DeathBringerSpell_Controller>().SetUpSpell(state);//设置
}
public bool canDoSpellCast()//是否可以进入施法状态
{
if (Time.time >= lastTimeCast + SpellCastStateCooldown)
{
return true;
}
else return false;
}
DeathBringerTeleportState脚本:
public override void Update()
{
base.Update();
if(triggerCalled)
{
if (enemy.canDoSpellCast())//如果可以施法则进入施法状态
enemyStateMachine.ChangeState(enemy.spellCastState);
else
enemyStateMachine.ChangeState(enemy.battleState);
}
}
创建DeathBringerSpellCastState脚本:
private Enemy_DeathBringer enemy;
private int amountsOfSpell;//法术实体数量
private float SpellTimer;//计时器
public override void Enter()
{
base.Enter();
amountsOfSpell = enemy.amoutsOfSpells;
SpellTimer =.5f;
}
public override void Update()
{
base.Update();
SpellTimer -= Time.deltaTime;
if(canCast())//可以施法则开始产生法术实体
{
enemy.CastSpell();
}
if(amountsOfSpell<=0)//所有法术实体释放完开始传送
{
enemyStateMachine.ChangeState(enemy.teleportState);
}
}
public override void Exit()
{
base.Exit();
enemy.lastTimeCast = Time.time;
}
private bool canCast()
{
if(amountsOfSpell>0&&SpellTimer<0)//有且不冷却
{
amountsOfSpell--;
SpellTimer = enemy.SpellCastCooldown;
return true;
}
else
return false;
}
DeathBringerIdleState脚本:
public override void Update()
{
base.Update();
if(Vector2.Distance(player.position, enemy.transform.position) < 7)//当玩家接近boss时开始战斗
{
enemy.BossFigthBegun = true;
}
if(stateTimer<0&&enemy.BossFigthBegun)
enemyStateMachine.ChangeState(enemy.battleState);
}