功能
- 按3如果没在用手榴弹,则拿出手榴弹。如果在用,则切换下一种手榴弹。没有则空手;
数据
- 新增手榴弹配表数据类,新建手榴弹配表,配表管理器新增手榴弹配表字典,初始化增加读取配表并转换成字典的代码,增加根据id得到手榴弹详情的方法;
- 手榴弹的预制体需要可拾取的、扔出的;
- 背包界面增加背包内的手榴弹、场景里的手榴弹、别人背包的手榴弹格子预制体和类;
- 人物有一个int grenadeIndex记录当前拿着的手榴弹在背包手榴弹列表的索引,没拿手榴弹时是-1;
- 每种手榴弹有一个待拾取预制体和一个生效预制体。待拾取预制体有一个手榴弹模型、一个触发器用来被玩家检测到,一个数据脚本记录自己是哪种手榴弹,没有投掷、爆炸相关功能。生效预制体有刚体碰撞体管理运动,投掷、爆炸相关功能。
用枚举记录手榴弹类型
public enum GrenadeType{
Frag,Smoke,Flash
}
手榴弹详细数据,这里不管类型,所有需要的数据都写在这里
[Serializable]
public class GrenadeDataBin:ItemDataBin{
public GrenadeType grenadeType;
public Grenade prefabThrowed;
public ParticleSystem explosionEffect;
public AudioClip explodeAudio;
public float explodeDelay=3;
public float lifeTimeAfterExplode;
public float explodeRadius=2;
public int damage;
}
逻辑
拾取和使用
- 在交互系统的拾取物品分支增加对手榴弹类的判断;
- 按拿出手榴弹的按键时
public void GetThrowInput(InputAction.CallbackContext input){ if(input.ReadValue<float>()==0){ switch(player.animator.GetInteger(CharacterBase.gunStatusPara)){ case CharacterBase.noWeaponState: case CharacterBase.rifleState: case CharacterBase.handgunState: player.UseGrenade(); break; case 3: player.PutAwayGun(); break; } } } - GetNextGrenade():在背包的手榴弹列表遍历,有和grenadeIndex类型不同的手榴弹则返回索引,没有则返回-1;
public int GetNextGrenade(){ if(grenadeInHandIndex==-1){//手里没有手榴弹 if(backpack.itemsInPack.grenadesInPack.Count>0){//背包里有,拿第一个 return 0; } else{ return -1;//背包里没有,不拿 } } for(int i=0;i<backpack.itemsInPack.grenadesInPack.Count;i++){//手里有手榴弹,切换下一种 if(backpack.itemsInPack.grenadesInPack[i].id!= backpack.itemsInPack.grenadesInPack[grenadeInHandIndex].id){ return i; } } return -1; } - UseGrenade(),先执行grenadeIndex=GetNextGrenade(),在右手实例化手榴弹,没有则回到空手;
public void UseGrenade(){
grenadeInHandIndex=GetNextGrenade();//去背包拿手榴弹
if(grenadeInHandIndex==-1){//没拿到
PutAwayGun();
return;
}
animator.SetInteger(gunStatusPara,3);
GrenadeDataBin grenadeDataBin=ItemDataManager.Instance.GetGrenadeData(
backpack.itemsInPack.grenadesInPack[grenadeInHandIndex].id);
grenadeInHand=Instantiate(grenadeDataBin.prefab,weaponHolder).GetComponent<Grenade>();
grenadeInHand.grenadeDataBin = grenadeDataBin;
grenadeInHand.owner=this;
}
- 扔手榴弹方法通过动画事件执行
public void ThrowGrenade(){
grenadeInHand.Throwed(aimAxis.forward*throwSpeed);
backpack.TakeOutGrenade(grenadeInHandIndex);
grenadeInHand=null;
grenadeInHandIndex=-1;
UseGrenade();//拿下一个或空手
}
物理
手榴弹只和地面层碰撞,不能碰撞人物,否则干扰扔出手榴弹的弹道。
爆炸效果
扔出一段时间后爆炸,生成粒子效果,播放爆炸声音, 粒子效果播放完后销毁。
public void Throwed(Vector3 velocity){
transform.SetParent(null);
_rigidbody.isKinematic=false;
_collider.isTrigger=false;
_rigidbody.velocity=velocity;
StartCoroutine(Explode());
}
IEnumerator Explode(){
yield return new WaitForSeconds(grenadeData.explodeDelay);
ParticleSystem effect=Instantiate(grenadeData.explosionEffect,transform.position,Quaternion.identity);
switch(grenadeData.grenadeType){
case GrenadeType.Frag://杀伤弹,爆炸后消失,特效存留一段时间
ExplodeDamage();
break;
}
if(grenadeData.explodeAudio){
MyAudioManager.Instance.MyPlaySound(effect.AddComponent<AudioSource>(),grenadeData.explodeAudio);
}
Destroy(effect.gameObject,effect.main.duration+effect.main.startLifetime.constant);
Destroy(gameObject,grenadeData.lifeTimeAfterExplode);
}
伤害判定
进行球形范围检测,检测到人物后再拿人物躯干和手榴弹做一次连线检测Physics.LineCast(),检测手榴弹和人物chest之间没障碍,再触发伤害。这里不再用人物的中枪触发器,只要人物碰撞体在爆炸范围内就受到一样的伤害。
void ExplodeDamage(){
Collider[] colliders=Physics.OverlapSphere(transform.position,
grenadeData.explodeRadius,1<<MyGameManager.characterLayer);
CharacterBase character;
for(int i=0;i<colliders.Length;i++){
if(colliders[i].TryGetComponent(out character)){
if(!Physics.Linecast(transform.position,character.
chest.position,1<<MyGameManager.groundLayer)){
character.TakeDamage(grenadeData.damage,"注意敌人");
}
}
}
}
声音
爆炸声音会在手榴弹爆炸并消失后持续一段时间,不能把声源放在手榴弹上并在爆炸时销毁或失活手榴弹,可以把声源放在粒子效果上。
人物动画
- 手榴弹动画放在上半身层,有一个空闲动画和投掷动画;
温雷功能
需求:按下鼠标拔环,拔环结束后如果没有松开则保持,开始计时。如果已经松开则抛出。
动画剪裁为拉环、抛出两段。
拉环和抛出各用一个trigger,按下鼠标设置拉环trigger,松开设置抛出trigger。这样即使立即按下抬起,抛出trigger也会保持着,直到拔环动作结束,直接出发抛出。

问题是在输入回调脚本和人物脚本分开的架构下,原本射击逻辑只关心鼠标是true还是false,现在扔雷要知道按下抬起的瞬间。要怎么修改?
我们想到inputSystem的回调函数就是在输入值改变时执行。但是还需要判断人物是不是拿着手雷,是则在执行时设置人物的那两个trigger。然后我发现按下鼠标时这个回调会执行2次,会把pullRing触发2次,但是并没有造成问题,第二次触发pullTrigger也被复原了?
public void GetFireInput(InputAction.CallbackContext input)
{
firePressed = input.ReadValue<float>() == 1;
if (firePressed)
{
player.StartPullTrigger();
}
else
{
player.StopPullTrigger();
}
}
2万+

被折叠的 条评论
为什么被折叠?



