当前位置:网站首页>Unity—物理引擎+“武器模块”
Unity—物理引擎+“武器模块”
2022-08-05 06:43:00 【小小数媒成员】
每日一句:保持热爱,奔赴下一场山海,愿大学生活,光芒万丈、快乐昂扬
目录
物理引擎
模拟真实世界中物体特性的引擎
刚体Rigidbodyˈrɪdʒɪd/
·带有刚体组件的游戏物体
刚体组件可使游戏对象受物理引擎控制,在受到外力时,产生真实世界中的运动(重力、弹力、摩擦力)
碰撞器collider/kəˈlaɪdə(r)/
物理材质
·用于调整碰撞对象的摩擦力和反弹效果
project—>(右键)Physic Material/məˈtɪəriəl/
属性:
动态摩擦力Dynamic Friction/daɪˈnæmɪk ˈfrɪkʃn/ 静态摩擦力Static Friction
弹力Bounciness
摩擦力合并模式Friction Conbine Mode
合并反弹 Bounce/baʊns/ Combine/kəmˈbaɪn/
平均值Average/ˈævərɪdʒ/ 最小Min 最大Max 相乘Multipy
碰撞条件
·两者都具有碰撞器组件
·运动的物体具有刚体组件
碰撞三阶段
进入碰撞时执行(接触的第一帧执行)
void OnCollisionEnter(Collision/kəˈlɪʒ(ə)n/ collother)
碰撞体与刚体接触时,每帧执行
void OnCollisionStay(Collision collother)
停止碰撞时执行
void OnCollisionExit(Collision collother)
private void OnCollisionEnter(Collision collother)
{ // collother:获取对方碰撞器组件
collother.collider.GetComponent< >
//获取第一个接触点
ContactPoint/ˈkɒntækt/ cp=collother.contacts[0];
//cp.point接触点的世界坐标
//cp.normal接触面法线
}
触发器
·碰撞器组件(collider),且Is Trigger属性被勾选的物体
现象:无碰撞效果
触发条件
·两者都具有碰撞组件
·其中一个带有刚体组件
·其中一个勾选Is Trigger
触发三阶段
当碰撞体进入触发器时执行
void OnTriggerEnter(Collider cldother)
当碰撞体与触发器接触时执行
void OnTriggerStay(Collider cldother)
当停止触发时执行
void OnTriggerExit(Collider cldother)
private void OnTriggerEnter(Collider collother)
{ // collother:就是对方碰撞器组件
collother.GetComponent< >
}
如果物体移动速度过快,碰撞检测失效—>解决方案:
开始时,使用射线检测
private void Start()
{
RaycastHit hit;
//重载15Raycast(起点坐标,方向,受击物体信息,距离,检测的层)
if(physics.Raycast(this.transform.position,this.transform.forward,out hit,100,mask))
{//检测到物体
targetPos=hit.point;//击中的位置
}
else
{//没有检测到物体
targetPos=this.transform.position+this.transform.forward*100;//物体正前方100米
}
}
void Update()
{ this.transform.position=
Vector3.MoveTowards(this.transform.position,targetPos,Time.deltaTime*100);
if((this.transform.position-targetPos).sqrMagnitude/ˈmæɡnɪtjuːd/<0.1f)
{print(“接触目标点”);
Destory(this.gameObject);//销毁子弹
}
武器模块
枪
策划
·如果弹匣内装有子弹,可以发射;否则等待更换弹匣
·发射子弹时,播放音效、动画、显示火花
·玩家的枪可以单发或连发
需求分析
·创建脚本—枪Gun,提供开火,更换弹匣功能
·创建脚本—单发枪SingleGun,继承自Gun,根据玩家输入调用相应(开关、更换弹匣)方法
·创建脚本—连发枪AutomaticGun,继承自Gun
子弹
策划
主角子弹
·根据击中敌人的部位减血
·子弹飞行到目标点销毁,并创建相应特效
敌人子弹
·击中玩家后减血
·子弹飞行到目标点销毁,并创建相应特效
·朝向玩家头部发射,飞行速度较慢,便于玩家躲避
需求分析:
·创建脚本—子弹Bullet,计算攻击的目标点,执行移动,创建接触特效
·创建脚本—玩家子弹PlayerBullet,继承自Bullet,根据击中敌人部位减血
·创建脚本—敌人子弹EnemyBullet,继承自Bullet,根据击中玩家后减血
EnemyAI类(续)
private Gun gun;
private void Start()
{gun=GetComponentInChildren<Gun>();}
Attack()
public float delay;
motor.LookRotation(playerStatusInfo.Instance.headTF.position);
if(atkTimer<=Time.time)
{Invoke(“Shoot”,delay);}
private void Shoot()
{ {//发射子弹(建议使用动画事件替代)
//发起攻击(从枪口位置指向玩家头部位置)
gun.Firing(playerStatusInfo.Instance.headTF.position-gun.firePointTF,position);
EnemyMotor类(续)
public void LookRotation(Vector3 targetPoint)
{//当前物体注视目标点旋转
this.transform.LookAt(targetPoint);//会出现头部高于自身物体 人物倾斜,旋转速度过快,解决—>
targetPoint.y=this.transform.position.y;
或
Quaternion dir = Quaternion.LookRotation(targetPoint - this.transform.position);
Quaternion rotate = Quaternion.Lerp(this.transform.rotation, dir, 20 * Time.deltaTime);
Vector3 euler = rotate.eulerAngles;
//仅仅沿Y轴旋转
this.transform.eulerAngles = new Vector3(0, euler.y, 0);
上波代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Bullet : MonoBehaviour
{ [HideInInspector]
public float atk;
/// <summary>
/// 攻击距离
/// </summary>
public float attackDistance = 20;
/// <summary>
/// 射线检测层
/// </summary>
public LayerMask mask;
protected RaycastHit hit;//受击目标信息
private Vector3 targetPos;
/// <summary>
/// 移动速度
/// </summary>
public float moveSpeed=200;
//计算目标点
private void CalculateTargetPoint()
{
//当前位置(枪口位置,当前方向(枪口方向),受击目标信息,攻击最大距离,射线检测层)
if (Physics.Raycast(this.transform.position, this.transform.forward, out hit, attackDistance, mask))
targetPos = hit.point;
else
targetPos = this.transform.position + this.transform.forward * attackDistance;
}
//移动
private void Movement()
{
this.transform.position = Vector3.MoveTowards(this.transform.position, targetPos, moveSpeed * Time.deltaTime);
}
//到达目标点;销毁,创建相关特效
//根据目标点物体的标签hit.collider.tag创建特效
private void Awake()
{
CalculateTargetPoint();
}
// Update is called once per frame
void Update()
{
Movement();
//如果到达目标点
if((this.transform.position-targetPos).sqrMagnitude<0.1f)
{
Destroy(this.gameObject);
GenerateContactEffect();
}
}
/// <summary>
/// 生成特效
/// </summary>
private void GenerateContactEffect()
{
if (hit.collider == null) return;
//根据目标物体标签创建相应特效
//switch(hit.collider.tag)【弊端】每次增加标签都得创建代码
//{
// case "":
// break;
//}
//特效名称:存放路径+接触物体标签
//资源较多,通过代码读取资源(用Recources读取,资源必须放到Recources目录下)
//根据标签加载资源(消耗性能)【建议使用对象池替代】
GameObject prefabGo = Recources.Load<GameObject>("目录/"+hit.collider.tag);
if(prefabGo)
//创建资源(资源预设体,目标点位置向法线方向移动0.01米,z轴朝向法线方向)
Instantiate(prefabGo,targetPos+hit.normal*0.01f,Quaternion.LookRotation(hit.normal));
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 玩家状态信息类
/// </summary>
public class playerStatusInfo : MonoBehaviour
{
public static playerStatusInfo Instance { get; private set; }
private void Awake()
{
Instance = this;//把当前对象的引用放进来
}
public float HP = 1000;
public float maxHP = 1000;
public Transform headTF;//玩家头部位置变换
public void Damage(float amount)
{
HP -= amount;
if(HP<=0)
{
Death();
}
}
public void Death()
{
//游戏结束
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyBullet :Bullet
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
private void OnTriggerEnter(Collider other)
{
//如果与玩家接触
if(other.tag=="标签")
{
//玩家减血
//调用玩家的受伤方法,把攻击力传进去
playerStatusInfo.Instance.Damage(atk);
//销毁子弹
Destroy(this.gameObject);
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class playerBullet : Bullet
{
//根据敌人部位减血(希望到达物体上再减血,需要使用委托)
// Start is called before the first frame update
void Start()
{
float atk = CalculateAttackForce();
//base.hit.collider.name;//击中敌人部位的名称
if(hit.collider!=null&&hit.collider.tag=="Enemy")
hit.collider.GetComponentInParent<EnemyStatusInfo>().Damage(atk);
}
// Update is called once per frame
void Update()
{
}
//计算攻击力
private float CalculateAttackForce()
{
//【建议】使用配置文件替换
//根据受击物体部位名称
switch(hit.collider.name)
{
case "脑袋名称"://击中头部
return atk * 3;
case "身体名称":
return atk * 1.5f;
default:
return atk;
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(AudioSource))]
public class Gun : MonoBehaviour
{
/// <summary>
/// 攻击力
/// </summary>
public float atk = 100;
public GameObject BulletPrefab;//需要发射的子弹预设体
private AudioSource audioSaurce;//声音源
public AudioClip clip;//发射子弹时的音频片段
private GunAnimation anim;
private MuzzleFlash muzzleFlash;
protected virtual void Start()
{
anim = GetComponent<GunAnimation>();
}
// Update is called once per frame
void Update()
{
}
/// <summary>
/// 开火
/// </summary>
/// <param name="direction">子弹朝向</param>
public void Firing(Vector3 direction)
{
//玩家枪发射:枪口方向
//敌人发射:从枪口位置朝向玩家头部位置
//如果敌人枪没有动画,则不调用准备子弹方法,如果准备子弹失败
if (anim!=null&&Ready() == false) return;
//判断弹匣内是否包含子弹
//发射子弹
//播放音频、动画
audioSaurce.PlayOneShot(clip);
if(anim)anim.action.Play(anim.fireAnimName);
muzzleFlash.DisplayFlash();
//创建子弹(instantiate创建出来的是Object转换成GameObject)
GameObject bulletgo=Instantiate(BulletPrefab, firePoint.position, Quaternion.LookRotation(direction))as GameObject;
//传递信息
bulletgo.GetComponent<Bullet>().atk = atk;
}
/// <summary>
/// 准备子弹
/// </summary>
/// <returns></returns>
private bool Ready()
{
//如果弹匣内没有子弹或更换弹匣动画正在播放
if (currentAmmoNullets <= 0 || anim.action.Isplaying(anim.UpdateAmmoAnimeName))
return false;
//减少弹匣内子弹数
currentAmmoNullets--;
//如果缺少子弹播放缺少子弹动画
if (currentAmmoNullets == 0)
anim.action.play(anim.lackBulletAnimeName);
return true;
}
public int ammolapacity = 15;//弹匣容量
public int currentAmmoNullets = 15;当前弹匣内子弹数(15)
public int ramainBullets = 90;//剩余总子弹数(90)
/// <summary>
/// 更换弹匣
/// </summary>
public void UpdateAmmeo()
{
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AutomaticGun : Gun
{
// Start is called before the first frame update
protected override void Start()
{
base.Start();
//子类做的事
}
// Update is called once per frame
void Update()
{
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SingleGun : Gun
{
private Transform firePoint;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if(Input.GetMouseButtonDown(0))
{
base.Firing(firePoint);
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 枪动画
/// </summary>
public class GunAnimation : MonoBehaviour
{
//开枪动画
public string fireAnimName = "fire";
//更换弹匣动画
public string UpdateAmmoAnimeName = "updateAmmo";
//缺少子弹动画
public string lackBulletAnimeName = "lackBullet";
public AnimationAction action;
private void Awake()
{
//动画组件在孩子身上,怎么找呢?——>
action = new AnimationAction(GetComponentInChildren<Animation>());
}
}
边栏推荐
猜你喜欢
随机推荐
Redis
Invalid operator for data type.The operator is add and the type is text.
UDP group (multi)cast
protobuf is compiled against the associated .proto file
性能提升400倍丨外汇掉期估值计算优化案例
400 times performance improvement 丨 swap valuation optimization case calculation
An IP conflict is reported after installing the software on a dedicated computer terminal
二叉搜索树问题
MySQL:order by排序查询,group by分组查询
软件测试必问面试题(附答案和解析)
MySQL: order by sorting query, group by grouping query
DNSlog外带数据注入
Rapid Medical's Ultra-Small and Only Adjustable Thromb Retriever Receives FDA Clearance
铠侠携手Aerospike提升数据库应用性能
MAYA大炮建模
How to avoid online memory leaks
Put Cloudflare on the website (take Tencent Cloud as an example)
Mysql主从延迟的原因和解决方案
Flink学习10:使用idea编写WordCount,并打包运行
GAN生成动漫头像Pytorch