下面的脚本使用一个函数,而首先检查范围内是否存在对象,如果存在,则为对象。
按下鼠标键时,弹丸朝着该物体的枢轴点射击。我要弹丸
我是编码的新手,看不到要删除的代码和要添加的代码。
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.Events;
public struct ShootHit
{
public GameObject gameObject;
public Vector3 point;
}
[System.Serializable]
public class UnityEventShootHit : UnityEvent<ShootHit> { }
[DisallowMultipleComponent, AddComponentMenu("(つ♥v♥)つ/Useables/Shoot")]
public class ShootComponent : MonoBehaviour
{
[Header("Input")]
[Tooltip("Data that determines the input of player actions")]
[SerializeField] private InputProfile _inputProfile;
// [SerializeField] private float _range = 100.0f;
/* [Header("Sight Values")]
[Tooltip("How far the the sight can reach")]
public float sightRadius = 1f;
[Range(0f, 360f)]
public float fieldOfViewAngle = 100f;*/
[Header("Charge-up")]
[SerializeField] private float _chargeupTime = 0.5f;
private bool _isChargingPrimary = false;
private bool _isChargingSecondary = false;
[Header("Aim Assist")]
[SerializeField] private LayerMask _aimAssistLayerMask;
public float aimAssistRadius = 30.0f; // radius
[Range(0.0f, 360.0f)]
public float aimAssistMaxAngleToAssist = 45.0f; // angle
private ShootHit? _target;
//publics
public Transform shootOrigin;
[Header("Events")]
public UnityEventShootHit OnPrimaryFire;
public UnityEvent OnPrimaryFireStart;
public UnityEvent OnPrimaryFireStop;
public UnityEventShootHit OnSecondaryFire;
public UnityEvent OnSecondaryFireStart;
public UnityEvent OnSecondaryFireStop;
private void Start()
{
if (_inputProfile == null) Debug.LogError(gameObject.name + " does not
have a player input");
}
private void Update()
{
// Remove target if object is too far away
if (_target.HasValue)
{
if (Vector3.Distance(_target.Value.gameObject.transform.position,
transform.position) > aimAssistRadius)
{
_target = null;
}
}
if (_inputProfile.GetPrimaryFireButtonDown())
{
StopCoroutine(ChargeUpBeforeFireSecondary());
if (!_isChargingPrimary)
{
StartCoroutine(ChargeUpBeforeFirePrimary());
}
}
else if (_inputProfile.GetSecondaryFireButtonDown())
{
StopCoroutine(ChargeUpBeforeFirePrimary());
if (!_isChargingSecondary)
{
StartCoroutine(ChargeUpBeforeFireSecondary());
}
}
if (_inputProfile.GetPrimaryFireButton() ||
_inputProfile.GetSecondaryFireButton())
{
if (!_target.HasValue) _target = GetObjectClosestToAim();
if (_inputProfile.GetPrimaryFireButton())
{
OnPrimaryFire.Invoke(_target.Value);
}
if (_inputProfile.GetSecondaryFireButton())
{
OnSecondaryFire.Invoke(_target.Value);
}
}
else
{
_target = null;
}
if (_inputProfile.GetPrimaryFireButtonUp())
OnPrimaryFireStop.Invoke();
if (_inputProfile.GetSecondaryFireButtonUp())
OnSecondaryFireStop.Invoke();
}
/// <summary>
/// Finds the object within range closest to the players forward-vector
using _aimAssistLayerMask.
/// </summary>
/// <returns>Returns object closest to aim if any object is found, else
returns null.</returns>
ShootHit? GetObjectClosestToAim()
{
// Raycast
RaycastHit hit;
if (Physics.Raycast(shootOrigin.position, Camera.main.transform.forward,
out hit, aimAssistRadius, _aimAssistLayerMask))
{
if (hit.transform?.GetComponent<IShootTarget>() != null)
{
Debug.Log(hit.transform.name);
return new ShootHit { gameObject = hit.transform.gameObject,
point = hit.point };
}
}
float _closestDot = -2f;
GameObject _closestDotObject = null;
RaycastHit[] _hit = Physics.SphereCastAll(transform.position,
aimAssistRadius, transform.forward, 0, _aimAssistLayerMask,
QueryTriggerInteraction.Ignore);
// Get best dot from all objects within range
for (int i = 0; i < _hit.Length; i++)
{
if (_hit[i].transform.gameObject == this.gameObject ||
_hit[i].transform.GetComponent<IShootTarget>() == null)
continue;
Vector3 _dif = _hit[i].transform.position - transform.position;
float _newDot = Vector3.Dot(transform.forward.normalized,
_dif.normalized);
if (_newDot > _closestDot)
{
_closestDot = _newDot;
_closestDotObject = _hit[i].transform.gameObject;
}
}
if (!_closestDotObject)
return null;
// Make sure there are no object in the way of our best-dot-object
Collider[] colliders = _closestDotObject.GetComponents<Collider>();
Vector3 point = colliders[0].ClosestPoint(shootOrigin.position);
float distanceToPoint = Vector3.Distance(shootOrigin.position, point);
// Get closest collider
for (int i = 1; i < colliders.Length; i++)
{
Vector3 newPoint = colliders[i].ClosestPoint(shootOrigin.position);
float newDistanceToPoint = Vector3.Distance(shootOrigin.position,
newPoint);
if (distanceToPoint > newDistanceToPoint)
{
point = newPoint;
distanceToPoint = newDistanceToPoint;
}
}
RaycastHit _rayhit;
if (Physics.Raycast(shootOrigin.position, point - transform.position,
out _rayhit, aimAssistRadius, _aimAssistLayerMask))
{
if (_rayhit.transform.gameObject != _closestDotObject)
{
return null;
}
}
Vector3 _vecToClosest = _closestDotObject.transform.position -
transform.position;
if (Vector3.Angle(transform.forward, _vecToClosest) <=
aimAssistMaxAngleToAssist)
{
return new ShootHit { gameObject = _closestDotObject, point = point
};
}
else
{
return null;
}
}
IEnumerator ChargeUpBeforeFirePrimary()
{
_isChargingPrimary = true;
yield return new WaitForSeconds(_chargeupTime);
_isChargingPrimary = false;
OnPrimaryFireStart.Invoke();
}
IEnumerator ChargeUpBeforeFireSecondary()
{
_isChargingSecondary = true;
yield return new WaitForSeconds(_chargeupTime);
_isChargingSecondary = false;
OnSecondaryFireStart.Invoke();
}
#if UNITY_EDITOR
private void OnDrawGizmosSelected()
{
if (!Application.isPlaying) return;
Color oldColor = Gizmos.color;
float halfFeildOfView = aimAssistMaxAngleToAssist * 0.5f;
float coneDirection = -90f;
Quaternion leftRayRotation = Quaternion.AngleAxis(-halfFeildOfView + coneDirection, Vector3.up);
Quaternion rightRayRotation = Quaternion.AngleAxis(halfFeildOfView + coneDirection, Vector3.up);
Vector3 leftRayDirection = leftRayRotation * transform.right * aimAssistRadius;
Vector3 rightRayDirection = rightRayRotation * transform.right * aimAssistRadius;
// Green Arc
Handles.color = new Color(0f, 1f, 0f, 0.25f);
Handles.DrawSolidArc(transform.position, Vector3.up, leftRayDirection, aimAssistMaxAngleToAssist, aimAssistRadius);
Gizmos.color = oldColor;
}
#endif
}
`
这是附属于弹丸的代码
using UnityEngine;
using System.Collections;
[RequireComponent(typeof(Rigidbody))]
public class ProjectileScript : MonoBehaviour
{
public GameObject Explosion;
public Transform Target;
Rigidbody _rigidbody;
public float speed = 1.0f;
void Start()
{
_rigidbody = GetComponent<Rigidbody>();
}
void FixedUpdate()
{
// _rigidbody.AddForce((Target.position - transform.position).normalized, ForceMode.Impulse);
Collider targetCollider = Target.GetComponent<Collider>();
Vector3 targetDirection;
if (targetCollider)
targetDirection = targetCollider.ClosestPoint(transform.position) - transform.position;
else
targetDirection = Target.position - transform.position;
_rigidbody.velocity = targetDirection.normalized * speed;
if (Vector3.Distance(transform.position, Target.position) < 1.0f)
{
//make the explosion
GameObject ThisExplosion = Instantiate(Explosion, gameObject.transform.position, gameObject.transform.rotation) as GameObject;
//destory the projectile
Destroy(gameObject);
}
}
public void SetTarget(GameObject target)
{
this.Target = target.transform;
}
public void SetTarget(ShootHit hit) => SetTarget(hit.gameObject);
}
此脚本是弹丸生成的方式和位置。它连接到位于枪口上的空游戏对象。
public class ProjectileSpawner : MonoBehaviour
{
public GameObject projectileInsert;
public GameObject projectileExtract;
public float projectileSpeed = 1.0f;
GameObject projectile;
public void Insert(GameObject target)
{
if (projectile) return;
projectile = Instantiate(projectileInsert, transform.position, Quaternion.identity);
ProjectileScript projectileScript = projectile.GetComponent<ProjectileScript>();
projectileScript.SetTarget(target);
projectileScript.speed = projectileSpeed;
// Physics.IgnoreCollision(projectile.GetComponent<Collider>(),
// projectileSpawn.parent.GetComponent<Collider>());
}
public void Extract(GameObject target)
{
if (projectile) return;
projectile = Instantiate(projectileExtract, target.transform.position, Quaternion.identity);
ProjectileScript projectileScript = projectile.GetComponent<ProjectileScript>();
projectileScript.SetTarget(gameObject);
projectileScript.speed = projectileSpeed;
// Physics.IgnoreCollision(projectile.GetComponent<Collider>(),
// projectileSpawn.parent.GetComponent<Collider>());
}
}
答案 0 :(得分:0)
您的问题有点含糊,看起来您在理解代码和尝试更改代码上付出了很多努力。无论如何,我会尽力而为。
始终能够发射(如果物体在射程范围内则无所谓)`
可能只需设置aimAssistRadius = 0;
或完全删除支票
if (Vector3.Distance(_target.Value.gameObject.transform.position, transform.position) > aimAssistRadius)
{
_target = null;
}
朝十字准线(到屏幕中点)射击,而不朝对象枢轴点
。
您发布的脚本(可能不是您的脚本)似乎具有做您不想要做的全部目的:寻求帮助。删除它可能会改变很多事情,但最简单的方法是在实例化弹丸的那一刻简单地设置ProjectileScript._rigidbody.velocity
。不幸的是,您没有提供发生这种情况的代码。
我看不到您的ShootComponent
与ProjectileScript
进行互动的那一刻,但可能是在其中的UnityEvent中...?
但是一般来说,它看起来可能只是
public class ProjectileScript : MonoBehaviour
{
public float speed = 1.0f;
private RigidBody _rigidbody;
private void Awake()
{
_rigidbody = GetComponent<RigidBody>();
}
public void SetDirection(Vector3 direction)
{
_rigidbody.velocity = direction.normalized * speed;
}
}
以及您将弹丸实例化的任何地方
var projectile = instantiatedObject.GetComponent<ProjectileScript>();
var direction = Camera.main.transform.forward;
projectile.SetDirection(direction);
如您所见,您将必须制作
if (Vector3.Distance(transform.position, Target.position) < 1.0f)
{
//make the explosion
GameObject ThisExplosion = Instantiate(Explosion, gameObject.transform.position, gameObject.transform.rotation) as GameObject;
//destory the projectile
Destroy(gameObject);
}
在其他地方出现,因为代码不再基于目标了……我可能会使用OnCollisionEnter
来代替例如
private void OnCollisionEnter(Collision collision)
{
// maybe only collide with a certain tag
if(collision.gameObject.tag != "Target") return;
//make the explosion
GameObject ThisExplosion = Instantiate(Explosion, gameObject.transform.position, gameObject.transform.rotation) as GameObject;
//destory the projectile
Destroy(gameObject);
}
答案 1 :(得分:0)
在ProjectileSpawner
中,添加一个LayerMask
字段以过滤掉非归宿弹丸的无效碰撞(在检查器中设置)。您将在检查器中进行设置,以使非归位弹丸与所需层碰撞:
public LayerMask collisionLayers;
在Insert
中,找到从屏幕中心发出的光线,并将其和LayerMask
用作SetTarget
的参数:
public void Insert(GameObject target)
{
if (projectile) return;
Ray centerRay = Camera.main.ScreenPointToRay(new Vector3(Screen.width/2, Screen.height/2, 0f));
projectile = Instantiate(projectileInsert, transform.position, Quaternion.identity);
ProjectileScript projectileScript = projectile.GetComponent<ProjectileScript>();
projectileScript.SetTarget(centerRay, collisionLayers);
projectileScript.speed = projectileSpeed;
// Physics.IgnoreCollision(projectile.GetComponent<Collider>(),
// projectileSpawn.parent.GetComponent<Collider>());
}
public void Extract(GameObject target)
{
if (projectile) return;
projectile = Instantiate(projectileExtract, target.transform.position, Quaternion.identity);
ProjectileScript projectileScript = projectile.GetComponent<ProjectileScript>();
projectileScript.SetTarget(gameObject);
projectileScript.speed = projectileSpeed;
// Physics.IgnoreCollision(projectile.GetComponent<Collider>(),
// projectileSpawn.parent.GetComponent<Collider>());
}
然后在ProjectileScript
中添加一个bool
字段以记住弹丸是否驻留在gameObject上或它是否跟随着光线,请添加一个Ray
字段以记住这样的光线,以及LayerMask
字段以记住要与之碰撞的内容:
public bool isHoming;
public Ray TargetRay
public LayerMask collisionLayers;
然后,在public void SetTarget(GameObject target)
中,将bool
设置为true
:
public void SetTarget(GameObject target)
{
this.Target = target.transform;
isHoming = true;
}
并创建一个新的public void SetTarget(Ray shootRay)
来记住Ray
和LayerMask
并将bool
设置为false
:
public void SetTarget(Ray shootRay, LayerMask collisionLayers)
{
this.TargetRay = shootRay;
this.collisionLayers = collisionLayers;
isHoming = false;
}
此外,在ProjectileScript
中,您将需要更改FixedUpdate
方法来检查布尔值是否正确,如果是,则与以前相同。否则,沿射线移动并在射线传播太远时将其摧毁:
public float maxDistanceBeforeDestroy = 100f;
...
void FixedUpdate()
{
if (isHoming)
{
// _rigidbody.AddForce((Target.position - transform.position).normalized, ForceMode.Impulse);
Collider targetCollider = Target.GetComponent<Collider>();
Vector3 targetDirection;
if (targetCollider)
targetDirection = targetCollider.ClosestPoint(transform.position) - transform.position;
else
targetDirection = Target.position - transform.position;
_rigidbody.velocity = targetDirection.normalized * speed;
if (Vector3.Distance(transform.position, Target.position) < 1.0f)
{
//make the explosion
GameObject ThisExplosion = Instantiate(Explosion, gameObject.transform.position, gameObject.transform.rotation) as GameObject;
//destory the projectile
Destroy(gameObject);
}
}
else
{
_rigidbody.velocity = TargetRay.direction.normalized * speed;
// Check if it has traveled too far
if ((transform.position - TargetRay.origin).magnitude > maxDistanceBeforeDestroy )
{
Destroy(gameObject);
}
}
}
然后,添加一个OnCollisionEnter
方法,如果弹头正在归位,该方法不会执行任何操作。但是,如果不是,它将检查碰撞是否与LayerMask
相匹配,如果匹配,则将爆炸并摧毁弹丸:
void OnCollisionEnter(Collision other)
{
if (isHoming)
{
return;
}
if( && ((1<<other.gameObject.layer) & collisionLayers) != 0)
{
//make the explosion
GameObject ThisExplosion = Instantiate(Explosion,
gameObject.transform.position,
gameObject.transform.rotation) as GameObject;
//destroy the projectile
Destroy(gameObject);
}
}