I'm trying to do RTS style movement with single selection only and without navmeshagent. When I click while another unit is already moving, its getting stopped and new one moving to same the point. Is there any way to seperate their hit points? Like I click to unit and order it to the point and then I click another unit and order him to another point.
Here is the video: demo
private Camera mycam;
private RaycastHit hit;
public Vector3 tf;
public LayerMask ground;
private bool move;
public static UnitMove instance;
private void Awake()
{
instance = this;
}
private void Start()
{
mycam = Camera.main;
}
private void Update()
{
if (Input.GetMouseButtonDown(1) && haveSelected())
{
if (UnitClick.Instance.selectChanged)
{
getMouseRay();
}
}
if (move)
{
SetDestination(UnitClick.Instance.selectedUnit.transform.position, hit);
}
}
private void SetDestination(Vector3 unitPos, RaycastHit hit)
{
move = true;
if (Vector3.Distance(UnitClick.Instance.selectedUnit.transform.position, hit.point) > 0.5f)
{
UnitClick.Instance.selectedUnit.transform.position = Vector3.MoveTowards(UnitClick.Instance.selectedUnit.transform.position,
new Vector3(hit.point.x, UnitClick.Instance.selectedUnit.transform.position.y, hit.point.z), 1f * Time.deltaTime);
}
}
public void getMouseRay()
{
Ray ray = mycam.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit, Mathf.Infinity, ground))
{
move = true;
}
}
private bool haveSelected()
{
if (UnitSelection.Instance.unitSelected.Count > 0)
{
return true;
}
else
{
return false;
}
}
}
Looks to me like rather than just setting a destination, the SetDestination() function actually moves UnitClick.Instance.selectedUnit every frame. Once you click another unit, UnitClick.Instance.selectedUnit presumably changes to the newly selected unit, meaning every object’s SetDestination() code is always moving the same single object.
You’ll want to do something like save the destination in an instance variable when SetDestination() is first called and then have the move code change either:
change transform.position if the script is attached to the object you want to move
or if not: save the current UnitClick.Instance.selectedUnit in an instance variable when SetDestination() is first called and then change its position in the move code.
Edit: Here’s an attempt to show what I mean but note that this assumes the script is attached to the GameObject you want to move. It also preserves your existing system of setting the destination every frame while the unit is selected (it may be more desirable to update it only when the mouse is clicked?)
private Camera mycam;
private RaycastHit hit;
public Vector3 tf;
public LayerMask ground;
private bool move;
private Vector3 destination;
public static UnitMove instance;
private void Awake()
{
instance = this;
}
private void Start()
{
mycam = Camera.main;
}
private void Update()
{
if (Input.GetMouseButtonDown(1) && haveSelected())
{
if (UnitClick.Instance.selectChanged)
{
getMouseRay();
}
}
if (move)
{
if( transform == UnitClick.Instance.selectedUnit.transform ) // If we are currently selected
SetDestination(/*UnitClick.Instance.selectedUnit.transform.position,*/ hit); // Update our destination
// If ‘move’ is true, move towards our destination whether or not we are currently selected
if (Vector3.Distance(transform.position, destination) > 0.5f)
transform.position = Vector3.MoveTowards(transform.position, destination, 1f * Time.deltaTime);
else
move = false; // This part is a guess at what you want — remove if it causes problems.
}
}
private void SetDestination(/*Vector3 unitPos,*/ RaycastHit hit)
{
move = true;
destination = new Vector3(hit.point.x, transform.position.y, hit.point.z);
}
public void getMouseRay()
{
Ray ray = mycam.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit, Mathf.Infinity, ground))
{
move = true;
}
}
private bool haveSelected()
{
if (UnitSelection.Instance.unitSelected.Count > 0)
{
return true;
}
else
{
return false;
}
}
Related
Alright so I have a collision script which works as in if I collide a gameobject with another object with the script attached it does register a collision.
The collision script I am using:
private void OnCollisionEnter(Collision collision)
{
Debug.Log("collision registered");
}
However when I try to collided my instantiated prefabs with my object with my script attached a collision does not get registered.
Does anyone know why this is? I think it's to do with with the prefabs being a "Moveable" but I can't quite get to the bottom of the problem.
Script attached to the prefabs:
public class Moveable : MonoBehaviour
{
public float _speedMetersPerSecond = 50f;
public float resistance = 10f;
public float current = 0f;
public List<Moveable> moveables = new List<Moveable>();
private Vector3? _destination;
private Vector3 _startPosition;
private float _totalLerpDuration;
private float _elapsedLerpDuration;
private Action _onCompleteCallback;
public GameObject Electron;
public Transform Lightbulb;
public SliderChange SliderScript;
public VMT2Counter script2;
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
// THE LIGHTBULB PART IS FOR THE SPAWN POINT SO DO NOT DELETE
Moveable NextOnPath = Instantiate(Electron, Lightbulb.position, Quaternion.identity).GetComponent<Moveable>();
moveables.Add(NextOnPath.GetComponent<Moveable>());
MoverController.instance.targets.Add(NextOnPath.GetComponent<Moveable>());
}
if (_destination.HasValue == false)
return;
if (_elapsedLerpDuration >= _totalLerpDuration && _totalLerpDuration > 0)
return;
_elapsedLerpDuration += Time.deltaTime;
float percent = (_elapsedLerpDuration / _totalLerpDuration);
transform.position = Vector3.Lerp(_startPosition, _destination.Value, percent);
if (_elapsedLerpDuration >= _totalLerpDuration)
_onCompleteCallback?.Invoke();
// resistance = SliderScript.slider.value;
// current = script2.PotentialDifference / resistance;
}
public void MoveTo(Vector3 destination, Action onComplete = null)
{
var distanceToNextWaypoint = Vector3.Distance(transform.position, destination);
_totalLerpDuration = distanceToNextWaypoint / _speedMetersPerSecond;
_startPosition = transform.position;
_destination = destination;
_elapsedLerpDuration = 0f;
_onCompleteCallback = onComplete;
}
private void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.name == "Resistor")
{
Debug.Log("lol");
_speedMetersPerSecond = 25f;
}
}
}
Another script which goes hand in hand with the Moveable script but is not attached to the prefab:
public class MoverController : MonoBehaviour
{
public List<Moveable> targets;
[SerializeField] private Moveable target;
private List<Transform> _waypoints;
private int _nextWaypointIndex;
public static MoverController instance;
void Awake()
{
if (instance == null)
{
instance = this;
}
else
{
Destroy(gameObject);
return;
}
DontDestroyOnLoad(gameObject);
}
private void OnEnable()
{
MoveToNextWaypoint();
}
private void MoveToNextWaypoint()
{
for (int i = 0; i < targets.Count; i++)
{
if (targets[i] == null) continue;
_waypoints = GetComponentsInChildren<Transform>().ToList();
_waypoints.RemoveAt(0);
var targetWaypointTransform = _waypoints[_nextWaypointIndex];
targets[i].MoveTo(targetWaypointTransform.position, MoveToNextWaypoint);
targets[i].transform.LookAt(_waypoints[_nextWaypointIndex].position);
_nextWaypointIndex++;
if (_nextWaypointIndex >= _waypoints.Count)
_nextWaypointIndex = 0;
}
}
}
[![A picture of the prefab][1]][1]
[1]: https://i.stack.imgur.com/uyuge.png*emphasized text*
Video which shows situation: https://clipchamp.com/watch/MXjnTKl2cqg
The collider is a trigger:
Collider A
Collider B
Event triggered
Collider
Collider
Collision
Collider
Trigger
Trigger
Trigger
Collider
Trigger
Trigger
Trigger
Trigger
To fix this problem, either uncheck the box for it to collide normally, or change your event handler to detect triggers instead of colliders:
private void OnTriggerEnter(Collider other) {
Debug.Log("collision registered");
}
See here for more https://docs.unity3d.com/ScriptReference/Collider.OnTriggerEnter.html :)
It is also worth pointing out that if neither of your objects have rigidbodies on, the event will not fire. If you need it to fire but don't want all of the rigidbody physics, you can add one but tick the isKinematic box.
I am doing a school project in Unity. My team and I decided to make an endless runner 2D game. However, because it is the first time I use C#, I don't know how to make my player's speed accelerate when collide with a game object in Unity. I only know how to destroy the player's health when a collision happens. Please help me! Thank you!
it's quite hard to give answers without seeing any code you've written, but as it's 2D and you've already got the collision damage working, you've probably used an OnCollisionEnter(), well it's very similar: you test if you've collided (which you've already done), then you add force to your player using a rigidbody2d, probably something along the lines of:
public Rigidbody2D rb;
void OnCollisionEnter2D(Collision2D collision)
{
rb.AddForce(direction * force, ForceMode2D.Impulse); // the ForceMode2D is
// optional, it's just so that
// the velocity change is sudden.
}
This should work.
If you have a GameManager that stores the game speed you can also do that too:
private float gameSpeedMultiplier = 0.5f;
void OnCollisionEnter(Collision collision)
{
if(collision.gameObject.CompareTag("Tag of the collided object")
{
GameManager.Instance.gameSpeed += gameSpeedMultiplier;
}
}
public class PlayerContoler : MonoBehaviour
{
public static PlayerContoler instance;
public float moveSpeed;
public Rigidbody2D theRB;
public float jumpForce;
private bool isGrounded;
public Transform GroundCheckPoint;
public LayerMask whatIsGround;
private bool canDoubleJump;
private Animator anim;
private SpriteRenderer theSR;
public float KnockbackLength, KnockbackForce;
private float KnockbackCounter;
public float bonceForce;
public bool stopImput;
private void Awake()
{
instance = this;
}
// Start is called before the first frame update
void Start()
{
anim = GetComponent<Animator>();
theSR = GetComponent<SpriteRenderer>();
}
// Update is called once per frame
void Update()
{
if(!PauseMenu.instance.isPause && !stopImput)
{
if (KnockbackCounter <= 0)
{
theRB.velocity = new Vector2(moveSpeed * Input.GetAxis("Horizontal"), theRB.velocity.y);
isGrounded = Physics2D.OverlapCircle(GroundCheckPoint.position, .2f, whatIsGround);
if (isGrounded)
{
canDoubleJump = true;
}
if (Input.GetButtonDown("Jump"))
{
if (isGrounded)
{
theRB.velocity = new Vector2(theRB.velocity.x, jumpForce);
AudioManager.instance.PlaySFX(10);
}
else
{
if (canDoubleJump)
{
theRB.velocity = new Vector2(theRB.velocity.x, jumpForce);
canDoubleJump = false;
AudioManager.instance.PlaySFX(10);
}
}
}
if (theRB.velocity.x < 0)
{
theSR.flipX = true;
}
else if (theRB.velocity.x > 0)
{
theSR.flipX = false;
}
} else
{
KnockbackCounter -= Time.deltaTime;
if(!theSR.flipX)
{
theRB.velocity = new Vector2(-KnockbackForce, theRB.velocity.y);
} else
{
theRB.velocity = new Vector2(KnockbackForce, theRB.velocity.y);
}
}
}
anim.SetFloat("moveSpeed", Mathf.Abs(theRB.velocity.x));
anim.SetBool("isGrounded", isGrounded);
}
public void Knockback()
{
KnockbackCounter = KnockbackLength;
theRB.velocity = new Vector2(0f, KnockbackForce);
anim.SetTrigger("hurt");
}
}
I tried by making a script to when I tap on a object on my scene to make sound. So, I have audio source and my script implemented on the object. However, with my script below it only deploys the first audio that i have which is (cup). What I want to change in my code or make it better is dont let audio run until I click on the object. and any way to do where I click on a object the audio triggers? please advise. thanks for the help.
Update: Right now, if I select everywhere the sound triggers and whenever I click on other object it gives the one audio of all of my object, that is not what I wanted.
here is my code:
public class TextToSpeech : MonoBehaviour
{
[SerializeField] AudioClip _audioClip;
[SerializeField] [Range(0.0f, 1.0f)] float _volume = 1;
AudioSource _audioSource;
public AudioClip SoundToPlay;
public float Volume;
public bool alreadyPlayed = false;
public bool playOnAwake = false;
private Touch touch;
private Vector2 beginTouchPosition, endTouchPosition;
private bool isPlaying;
void Start()
{
_audioSource = GetComponent<AudioSource>();
_audioSource.clip = _audioClip;
_audioSource.volume = _volume;
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
_audioSource.enabled = true;
if (!_audioSource.isPlaying) {
_audioSource.clip = SoundToPlay;
_audioSource.Play ();
}
else
{
_audioSource.enabled = false;
}
}
}
}
So, After realizing you problems I changed this answer.
At first your code must be attached only one gameobject, for example camera, like this
also I named my script StackOverflow, you can name it whatever you want.
And BoxCollider must be attached to your plastic bottle or cup, like this
you can set it to trigger or nontrigger(depended on your necessity) and set its size to fit the real size of your objects in the scene.
and at last this is the Script which I named StackOverFlow, and SpeechToText in your case
public class StackOverFlow : MonoBehaviour {
[SerializeField] AudioClip _audioClip;
[SerializeField] [Range(0.0f, 1.0f)] float _volume = 1;
AudioSource _audioSource;
bool isPlaying;
void Start() {
_audioSource = GetComponent<AudioSource>();
_audioSource.clip = _audioClip;
_audioSource.volume = _volume;
}
void Update() {
if (Input.touchCount > 0) {
Touch touch = Input.touches[0];
if (touch.phase == TouchPhase.Began) {
Ray touchRay = Camera.main.ScreenPointToRay(touch.position);
Physics.Raycast(touchRay, out RaycastHit hit, Mathf.Infinity);
if (hit.collider != null) {
if (hit.collider.name.Contains("Cup") || hit.collider.name.Contains("Bottle")) {
PlaySound();
}
else {
Debug.Log($"otherobject {hit.collider.name}");
}
}
}
}
}
private void PlaySound() {
if (!isPlaying) {
_audioSource.Play();
isPlaying = true;
StartCoroutine(CheckIfPlaying());
}
}
private IEnumerator CheckIfPlaying() {
yield return new WaitForSeconds(_audioClip.length);
isPlaying = false;
}
}
ask me in comments if something is unclear
So you seem to be misunderstanding the use of OnTriggerEnter, you would be better raycasting from the touch position and playing based on that
void Update()
{
Camera mainCam = Camera.main;
for (int i = 0; i < Input.touchCount; ++i)
{
Vector3 touchPos = mainCam.ScreenToWorldPoint(Input.GetTouch(i).position);
RaycastHit2D hit = Physics2D.Raycast(transform.position, Vector2.forward);
if(hit.gameObject == this.gameObject){
// play sound
}
}
}
Below script uses a function whereas there first is a check if there is a object in range and if it, it is.
On pressing mouse key, the projectile is shooting towards the pivot point of that said object. I want the projectile to
always being able to fire (indifferent if the object is in range) and
shoot towards the crosshair (to the screen midpoint) and NOT towards the object pivot point.
I am a novice at coding and I can't see what code to remove and what to add.
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
}
`
This is the code attached to the projectile
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);
}
This script is how and where the Projectile Spawns. It is attached to an empty gameobject located on the muzzle of the gun.
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>());
}
}
In the ProjectileSpawner, add a LayerMask field to filter out invalid collisions (set it in the inspector) for the non-homing projectile. You'll set this in the inspector to make the non-homing projectiles collide with the desired layers:
public LayerMask collisionLayers;
In Insert, find the ray going from the screen's center, and use it and the LayerMask as a parameter to 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>());
}
Then in the ProjectileScript, add a bool field to remember if the projectile homes on a gameObject or if it follows a ray, a Ray field to remember such a ray, and a LayerMask field to remember what to collide with:
public bool isHoming;
public Ray TargetRay
public LayerMask collisionLayers;
Then, in public void SetTarget(GameObject target), set that bool to true:
public void SetTarget(GameObject target)
{
this.Target = target.transform;
isHoming = true;
}
And make a new public void SetTarget(Ray shootRay) that remembers a Ray and LayerMask and sets the bool to false:
public void SetTarget(Ray shootRay, LayerMask collisionLayers)
{
this.TargetRay = shootRay;
this.collisionLayers = collisionLayers;
isHoming = false;
}
Also, in ProjectileScript, you will need to change the FixedUpdate method to check if the bool is true, and if it is, then do the same as before. Otherwise, move along the ray and destroy it if it travels too far:
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);
}
}
}
Then, add an OnCollisionEnter method that doesn't do anything if the projectile is homing. But if it isn't, it checks if the collision matches the LayerMask, and if it does, it makes the explosion and destroys the projectile:
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);
}
}
Your question is a bit vague and doesn't look like you have put a lot of effort into understanding the code and trying to alterate it. Anyway I'll do my best.
always being able to fire (indifferent if the object is in range)`
probably simply set aimAssistRadius = 0; or entirely remove the check
if (Vector3.Distance(_target.Value.gameObject.transform.position, transform.position) > aimAssistRadius)
{
_target = null;
}
shoot towards the crosshair (to the screen midpoint) and NOT towards the object pivot point.
The script you posted (probably not yours) seems to have the entire purpose of doing what you not want to do: Aim assist. Removing it probably changes a lot of things but the simpliest would be to simply set the ProjectileScript._rigidbody.velocity in the moment the projectile is instantiated. Unfortunately you didn't provide the code where this happens.
I don't see in which moment your ShootComponent interacts with the ProjectileScript but probably in one of those UnityEvents ... ?
But in general it would maybe simply look like
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;
}
}
and whereever you Instantiate the projectile do
var projectile = instantiatedObject.GetComponent<ProjectileScript>();
var direction = Camera.main.transform.forward;
projectile.SetDirection(direction);
as you can see you will have to make the
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);
}
happen somewhere else since the code will not be target based anymore ... I would probably use OnCollisionEnter instead something like e.g.
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);
}
this is my code for raycast in my school project game. If I put script on object everything is working just fine. But if I close Unity and reopen my project, the value of "jakDaleko" = distance stays locked on 1129.395 instead of changing every frame.
What should I change so it will work everytime and not just the first time ipress play button.
Here's my code.
script 1 = raycast
public class SmerDivani : MonoBehaviour {
public static float VzdalenostOdCile;
public float VzdalenostOdCileInterni;
// Update is called once per frame
void Update() {
RaycastHit Hit;
if (Physics.Raycast(transform.position, transform.TransformDirection(Vector3.forward), out Hit)) {
VzdalenostOdCileInterni = Hit.distance;
VzdalenostOdCile = VzdalenostOdCileInterni;
}
}
}
Second script
public class TabuleMesto1 : MonoBehaviour
{
public float JakDaleko;
public GameObject AkceTlacitko;
public GameObject AkceText;
public GameObject UIQuest;
public GameObject ThePlayer;
public GameObject NoticeCam;
void Update() {
JakDaleko = SmerDivani.VzdalenostOdCile;
}
void OnMouseOver() {
if (JakDaleko <= 5) {
AkceTlacitko.SetActive(true);
AkceText.SetActive(true);
}
if (JakDaleko > 5)
{
AkceTlacitko.SetActive(false);
AkceText.SetActive(false);
}
if (Input.GetButtonDown("Akce")) {
if (JakDaleko <= 5) {
AkceTlacitko.SetActive(false);
AkceText.SetActive(false);
UIQuest.SetActive(true);
NoticeCam.SetActive(true);
ThePlayer.SetActive(false);
}
}
}
void OnMouseExit() {
AkceTlacitko.SetActive(false);
AkceText.SetActive(false);
}
}
I'm not quite sure what you're trying to achieve? Maybe this should "fix" your problem, you're not clearing the distance if the raycast don't hits....
void Update() {
RaycastHit Hit;
if (Physics.Raycast(transform.position, transform.forward, out Hit)) {
VzdalenostOdCileInterni = Hit.distance;
}
else {
VzdalenostOdCileInterni = 0.0f;
}
VzdalenostOdCile = VzdalenostOdCileInterni;
}
Additionally I think you should use transform.forward instead of transform.TransformDirection(Vector3.forward)
I think that the biggest problem was the name of the file. For some reason, my stupidity included, the solution was to rename the script from table1 to table_1