I have the following code, but i'm having some problems. Every time update is called, the character move to the point i give him to. But i just give him a point when i click with the mouse on the ground, the problem starts when i try to make it animate the character.
If i pass the value of the animation on the clickToMove() method it'll always play that animation even if we aren't moving. And if i place clickToMove inside the "if clicked" the character will teleport and not move towards. I can't think in a way to do the animation properly, only when the object is moving, and goes back to idle when it's stopped, even if the clickToMove() is playing all the time.
using UnityEngine;
using System.Collections;
public class ClickScript : MonoBehaviour
{
public float moveSpeed;
public float minDistance;
Vector3 mouseClick; //Creates a variable to save the constant of the hit from raycast
private Animator anim;
private Rigidbody rigidB;
// Use this for initialization
void Start()
{
anim = GetComponent<Animator>();
rigidB = GetComponent<Rigidbody>();
}
// Update is called once per frame
void FixedUpdate()
{
if (Input.GetMouseButtonDown(1))//If clicked
{
clickPosition(); //Gets the click position
}
if (Vector3.Distance(transform.position, mouseClick) > minDistance) //If the click distance is bigger than the minimal
{
//It is allways moving, but since there's no click position it doesn't move when not clicked
clickToMove();
}
}
void clickPosition()//This function throw a raycast on the ground and save the position on mouseClick to make the char move to there
{
RaycastHit clickHit; //creates a constant with the infos from the raycast
Ray mouseClickPosition = Camera.main.ScreenPointToRay(Input.mousePosition); //creates a constant to save the mouse position
if (Physics.Raycast(mouseClickPosition, out clickHit, 100.00f))//throw a raycast returning the mouse position and more infos
{
Debug.Log("Click at " + clickHit.point); //Show where were clicked
mouseClick = clickHit.point;//mouseClick receive the position of the click
}
}
void clickToMove() //this function make the player look at the click and move to mouseClick
{
mouseClick.y = transform.position.y; //get rid of the y to fix rotation bugs
transform.LookAt(mouseClick);//look at the poit you clicked
transform.position = Vector3.MoveTowards(transform.position, mouseClick, moveSpeed * Time.deltaTime);//move to the clickpoint
}
}
How about this way?
bool _isAnimating = false;
// Update is called once per frame
void FixedUpdate()
{
if (Input.GetMouseButtonDown(1))//If clicked
{
clickPosition(); //Gets the click position
//start animation here!!!
//animator.SetBool("bWalk", false);
//and set animation state to true
_isAnimating = true;
}
if (Vector3.Distance(transform.position, mouseClick) > minDistance)
{
clickToMove();
}
else if(_isAnimating)
{
//turn off the animation here!!!
//animator.SetBool("bStop", false);
//and set to false
_isAnimating = false;
}
}
Related
I'm making a top-down game where you drive a car and shoot targets at the same time. I have a script that makes a sprite of a crosshair follows the mouse cursor and I want to have it set up so that when the player presses the mouse button (the mouse button isn't in the code now) and the crosshair sprite is overlapping an enemy sprite, the enemy dies. I was following this documentation on Bounds.Intersects. Here's my code:
public class shootingScript : MonoBehaviour
{
public GameObject target, enemy;
CircleCollider2D targetCollider;
CapsuleCollider2D enemyCollider;
// Start is called before the first frame update
void Start()
{
//Check that the first GameObject exists in the Inspector and fetch the Collider
if (target != null)
{
print("targ not null");
targetCollider = target.GetComponent<CircleCollider2D>();
}
//Check that the second GameObject exists in the Inspector and fetch the Collider
if (enemy != null)
{
print("enemy not null");
enemyCollider = enemy.GetComponent<CapsuleCollider2D>();
}
}
// Update is called once per frame
void Update()
{
if (targetCollider.bounds.Intersects(enemyCollider.bounds))
{
print("hit");
Destroy(enemy);
}
}
}
In-game "targ not null" and "enemy not null" prints but when I move my cursor and the crosshair goes over the enemy "hit" is not printed and the enemy is not destroyed. I have a CircleCollider2D on the crosshair and a CapsuleCollider2D on the enemy. The script is on an empty game object. I also tried sprite.bounds but that resulted in the enemy getting killed as soon as I run the game.
EDIT:
Here's the code that keeps the crosshair sprite on the cursor. I copied it from somewhere. I set moveSpeed to 99999 since I want the crosshair sprite to be exactly where the mouse is.
public class mouseReticle : MonoBehaviour
{
private Vector3 mousePosition;
public float moveSpeed = 0.1f;
// Use this for initialization
void Start()
{
}
// Update is called once per frame
void Update()
{
mousePosition = Input.mousePosition;
mousePosition = Camera.main.ScreenToWorldPoint(mousePosition);
transform.position = Vector2.Lerp(transform.position, mousePosition, moveSpeed);
}
}
The target sprite and enemy sprite were at different depths (z).
I am very new to unity and am building a VR app for Oculus Go. I want to pick and move the object by pointing the ray from the controller on the object and then picking or releasing it by pressing the trigger button. I want the object to stay fixed at the end of the ray's position rather than coming suddenly onto the controller. I have used this script to create a ray and basically allow the controller to pick it up but this script shits the object to the controller's position and as a result I can only move object in a circle(in 360 degrees). It also does not drop the object correctly, as the objects continue to float.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerPointer : MonoBehaviour {
//Returns whatever object is infrount of the controller
private GameObject pointerOver;
[SerializeField]
//Is the object that is currently intractable
private PropBase selectedObject;
//Is the object currently stored in hand, ready to throw.
[SerializeField]
private PickUp inHand;
//This is a refrance to the object we want the pointer to be cast from.
[SerializeField]
public Transform controllerRef;
//This is where we want object we are holding to appear
[SerializeField]
private Transform holdingRef;
//The amount of force we want to throw objects from our hand with.
[SerializeField]
[Range(2,12)]
private float throwForce = 10;
//The script that handles the visuals to show what object is selected
[SerializeField]
private HighlightObject selectVisual;
private LineRenderer line;
void Start () {
line = GetComponent<LineRenderer> ();
}
void Update () {
//If a object is currently being held I don't want to select another
object until it is thrown.
if (inHand == null) {
WorldPointer ();
} else {
line.SetPosition (0, controllerRef.position);
line.SetPosition (1, controllerRef.position);
pointerOver = null;
}
//This function handles how you intract with selected objects
Intract ();
}
//This function handles shooting a raycast into the world from the
controller to see what can be intracted with.
void WorldPointer(){
//We set the line visual to start from the controller.
line.SetPosition (0, controllerRef.position);
RaycastHit hit;
//We reset the pointer so things don't stay selected when we are
pointing at nothing.
pointerOver = null;
//This sends a line from the controller directly ahead of it, it returns
true if it hits something. Using the RaycastHit we can then get information
back.
if (Physics.Raycast (controllerRef.position, controllerRef.forward, out
hit)) {
//Beacuse raycast is true only when it hits anything, we don't need
to check if hit is null
//We set pointerOver to whatever object the raycast hit.
pointerOver = hit.collider.gameObject;
//We set the line visual to stop and the point the raycast hit the
object.
line.SetPosition (1, hit.point);
//Here we check if the object we hit has the PropBase component, or
a child class of its.
if (pointerOver.GetComponent<PropBase> ()) {
//We set the object to be highlighted
selectVisual.NewObject (pointerOver);
} else {
selectVisual.ClearObject ();
}
} else {
//If the raycast hits nothing we set the line visual to stop a
little bit infrount of the controller.
line.SetPosition (1, controllerRef.position + controllerRef.forward
* 10);
selectVisual.ClearObject ();
}
Debug.DrawRay(controllerRef.position , controllerRef.forward *
10,Color.grey);
}
void Intract(){
//We set up the input "OculusTouchpad" in the Input manager
if (Input.GetButtonDown ("Jump") || OVRInput.GetDown
(OVRInput.Button.PrimaryTouchpad)) {
selectVisual.ClearObject ();
//Check if you are holding something you can throw first
if (inHand != null) {
inHand.Release (controllerRef.forward, throwForce);
inHand = null;
//We do this check here to prevent Errors if you have nothing
selected
} else if (selectedObject != null) {
//Check if you can pick up the selected object second
if (selectedObject.GetComponent<PickUp> ()) {
//Beacuse PickUp is a child of PropBase, we can ask InHand
to store selectedObject as PickUp, rather than use GetComponent
inHand = selectedObject as PickUp;
inHand.Store (holdingRef);
//If non of the above were valid then simple call the
trigger function of the selected object
} else {
selectedObject.Trigger ();
}
}
//If you have a object that you need to hold down a button to
intract with
} else if (Input.GetButton ("Jump") && selectedObject != null ||
OVRInput.Get (OVRInput.Button.PrimaryTouchpad) && selectedObject != null) {
selectedObject.Pulse ();
//When you are not pressing down the touchpad button, the selected
object can be updated
} else if (pointerOver != null) {
if (pointerOver.GetComponent<PropBase> ()) {
selectedObject = pointerOver.GetComponent<PropBase> ();
} else {
selectedObject = null;
}
} else {
selectedObject = null;
}
}
}
And i have attached this script to the objects I want to pick:
public class PickUp : PropBase
{
private Rigidbody rb;
void Start()
{
rb = GetComponent<Rigidbody>();
}
public virtual void Store(Transform NewParent)
{
//The following stops the object being effected by physics while it's in
the players hand
rb.isKinematic = true;
//And fixes it to the new parent it is given by the player script to
follow.
transform.parent = NewParent;
//It then resets it's position and rotation to match it's new parent
object
transform.localRotation = Quaternion.identity;
transform.localPosition = Vector3.zero;
}
public virtual void Release(Vector3 ThrowDir, float ThrowForce)
{
//On Release the object is made to be effected by physics again.
rb.isKinematic = false;
//Free itself from following it's parent object
transform.parent = null;
//And applies a burst of force for one frame to propel itself away from
the player.
rb.AddForce(ThrowDir * ThrowForce, ForceMode.Impulse);
}
}
What i'd like to see is have the position of the sphere change according to wherever the end of the ray is cast.
I have also attached this script to the player contoller, which allows it to move to a point by pointing to it and pressing the touchpad button.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ClickToMove : MonoBehaviour
{
private Vector3 targetPos; //This Vector3 will store the position where we
click to move.
private bool Moving = false; /*This bool keeps track of whether we are in
the process of moving or not.*/
private GameObject targetInstance;
/*The variables we want to customize. Added info headers to these for the
Unity Editor.*/
[Header("Our Go controller object")]
public GameObject goController;
[Header("Movement Speed")]
public float speed = 1;
[Header("Stop When This Far Away From Target")]
public float haltDistance = 0;
[Header("Optional Target Object")]
public GameObject targetObj;
void Update()
{
MoveToTarget(); /*Here we simply run our MoveToTarget method in the
Update method.*/
//That way we don't clutter up the Update method with too much code.
}
void MoveToTarget() //Here we do the cluttering instead.
{
var ray = new Ray(goController.transform.position,
goController.transform.forward); /*Create a ray going from the goController
position and in the Forward direction of the goController.*/
RaycastHit hitInfo; //Store info about what the ray hits.
Physics.Raycast(ray, out hitInfo, 100);
if (OVRInput.GetUp(OVRInput.Button.PrimaryTouchpad)) /*If we release the
trigger..*/
{
targetPos = hitInfo.point; /*Make our targetPos assume the
positional value of the hit point.*/
if (targetObj) /*If we have specified a Target Object to mark where
we click*/
//If we didn't, then we don't want to try to instantiate it.
{
if (targetInstance) /*If there is already a Target Object in the
scene.*/
{
Destroy(targetInstance); //Destroy it.
}
targetInstance = Instantiate(targetObj, targetPos,
transform.rotation); //Create our Target object at the position we clicked.
}
Moving = true; //And finally we set Moving to True.
}
if (Moving == true) //Since Moving is now true
{
transform.position = Vector3.MoveTowards(transform.position, new
Vector3(targetPos.x, transform.position.y, targetPos.z), speed *
Time.deltaTime); /*Transform our x and z position to move towards the
targetPos.*/
/*Note that our y position is kept at default transform position
since we only want to move along the ground plane.*/
}
if (Vector3.Distance(transform.position, targetPos) <= haltDistance + 1)
/*Check proximity to targetPos. Mainly useful to keep your player from
setting a target position right next to say a building and then end up
clipping through half of it.*/
{
if (targetInstance) //If we created a Target Object..
{
Destroy(targetInstance); //Then we want to destroy it when we
reach it.
}
Moving = false; //Since we have now arrived at our target
//destination.
}
}
}
If anyone could point me in a right direction or help me with this, I would greatly appreciate it!
Thanks in advance.
Okay, with your updated question its now possible to try and answer.
First off - have you tried not resetting your BaseProp localPosition to the controller's?
Try commenting the line that says
transform.localPosition = Vector3.zero;
This wil still orient the object and parent it to the controller but will lock it in a position relative to the moment of parenting.
You currently use "holdingRef" object as a place where the object appears. You may want to use "controllerRef" instead.
To vary distance at which the object appears you can set the object position to:
controllerRef.position+ distance*controllerRef.forward
As this is the direction in which you fire your raycasts. You can get the hit distance by querying hit.distance.
If for any reason that doesn't work out for you, the very point of the raycast hitting the collider is available within HitInfo, so with hit.point you can extract the hit position and position the object relative to that point. Another very useful attribute of hitinfo is .normal, which enables you to get direction at which the hit happened.
You can pass that info along with your Store method.
Goal: Detect when the cursor has entered a defined radius of the player.
Hello, I am in the process of trying to replicate the combat system from a game called CrossCode. The idea is that when the cursor is within a certain radius of the player, I will be able to switch to melee combat, and back to ranged once the cursor leaves this radius.
I have implemented one way I thought it could be done, however it feels slow or unreliable and I just wanted to know if there are any other methods I could possibly look into to achieve a smoother result.
Here is what I've done
attached to the player
void Update()
{
attackStyleSwitchRadius = colRef.radius;
playerCenter = colRef.transform.position;
if(Physics2D.OverlapCircle(playerCenter, attackStyleSwitchRadius, cursor))
{
meleeMode = true;
rangeMode = false;
}
else
{
meleeMode = false;
rangeMode = true;
}
}
And on a small 2D object I have this script so that it follows the cursor position.
void Update()
{
pos = Input.mousePosition;
gameObject.transform.position = Camera.main.ScreenToWorldPoint(pos);
}
when the small object enters the overlap circle it changes the bools around.
You can remove the collision detection overhead by doing something like this instead;
void Update ()
{
attackStyleSwitchRadius = colRef.radius;
playerCenter = colRef.transform.position;
var mouse = Input.mousePosition;
mouse.z = Vector3.Distance(Camera.main.transform.position, playerCenter);
var range = Vector2.Distance(Camera.main.ScreenToWorldPoint(mouse), playerCenter);
var inside = range < attackStyleSwitchRadius;
meleeMode = inside;
rangeMode = !inside;
}
Cursor
Make an object, name it cursor.
Add a small collider to the cursor object.
Add a script to the cursor object so its always at the mouses location.
Melee range zone
Add a GameObject as child of the player, name it MeleeRangeZone.
Add a collider to it, set it to be a Trigger. The size of this collider will be the players melee range,
Add a rigidbody to it so that collisions can be detected, but set the rigidbody to not rotate or change its position.
Add a script to the object and use the OnTriggerEnter and OnTriggerExit methods to detect whether or not the cursor has entered your melee zone.
You can now use the OnTriggerEnter and OnTriggerExit methods to switch between the players attack modes, meaning that when the cursor enters it changes to melee and when it exit it changes to ranged.
You can fire the ray to detect the location the cursor should have like this:
public LayerMask RaycastCollidableLayers;
public RaycastHit Hit;
public float CheckDistance = 200f;
public Transform Cursor;
void PerformRaycast(){
//Set up ray
ray = Camera.main.ScreenPointToRay(Input.mousePosition);
//Fire ray
Physics.Raycast(ray, out Hit, CheckDistance + 0.1f, RaycastCollidableLayers);
if (Hit.collider == null)
{
//Debug.Log("Raycast hit nothing");
return;
}
else //Ray hit something
{
//Move cursor to Hit.point;
Cursor.position = Hit.point;
}
}
I am pretty fluent in using Unity, but regarding Mechanim and Animations I am not the too great at currently, so please don't give me too much of a hard time lol. So I have this boolean that is in my GameManager script:
public bool countDownDone = false;
This boolean gets set to true once my "3,2,1, GO!" Countdown Timer ends at the beginning of my game. Everything in my game starts after that boolean is set to true. Example:
using UnityEngine;
using System.Collections;
public class PlaneMover : MonoBehaviour {
private GameManagerScript GMS; // Reference to my GameManager Script
public float scrollSpeed;
public float tileSizeZAxis;
public float acceleration; //This has to be a negative number in the inspector. Since our plane is coming down.
private Vector3 startPosition;
void Start () {
GMS = GameObject.Find ("GameManager").GetComponent<GameManagerScript> (); //Finds the Script at the first frame
// Transform position of the quad
startPosition = transform.position;
}
void Update () {
if (GMS.countDownDone == true) //Everything starts once the countdown ends.
{
/* Every frame - time in the game times the assigned scroll speed
and never exceed the length of the tile that we assign */
float newPosition = Mathf.Repeat (Time.time * scrollSpeed, tileSizeZAxis);
// New position equal to the start position plus vector3forward times new position
transform.position = startPosition + Vector3.forward * newPosition; // was vector3.forward
scrollSpeed += Time.deltaTime * acceleration; // This makes the plane increase in speed over time with
// whatever our acceleration is set to.
}
}
}
I have this Crawling animation that plays at the very beginning of the game (Even before the Timer ends) and loops forever. My question is , how do I make that crawling animation also start once that boolean is set to "true"? Or do I just apply it to a CoRoutine and make it play after 3 seconds? I have done extensive research on whether to reference Animation or Animator and I got confused on that too. Any advice? If you need more pictures or details on my question, just let me know. Thank you! :)
NEW CODE BELOW
using UnityEngine;
using System.Collections;
public class Crawling : MonoBehaviour {
Animator animator;
private GameManagerScript GMS;
void Start ()
{
animator = GetComponent<Animator> ();
GMS = GameObject.Find ("GameManager").GetComponent<GameManagerScript> ();
}
void Update ()
{
if (GMS.countDownDone == true)
{
animator.Play("Main Character Crawling", 1);
}
}
}
I have this Crawling animation that plays at the very beginning of the
game (Even before the Timer ends) and loops forever. My question is ,
how do I make that crawling animation also start once that boolean is
set to "true"?
The solution is to play the animation from script.
Remove the currentAnimation.
Select the GameObject your PlaneMover script is attached to, attach Animation and Animator components to it. Make sure that the Animation's Play Automatically is unchecked.
public class PlaneMover : MonoBehaviour {
private GameManagerScript GMS; // Reference to my GameManager Script
public float scrollSpeed;
public float tileSizeZAxis;
public float acceleration; //This has to be a negative number in the inspector. Since our plane is coming down.
private Vector3 startPosition;
Animation animation;
public AnimationClip animationClip; //Assign from Editor
void Start () {
GMS = GameObject.Find ("GameManager").GetComponent<GameManagerScript> (); //Finds the Script at the first frame
// Transform position of the quad
startPosition = transform.position;
animation = GetComponent<Animation>();
//Add crawing Animation
animation.AddClip(animationClip, "Crawling");
//Add other animation clips here too if there are otheres
}
void Update ()
{
if (GMS.countDownDone) //Everything starts once the countdown ends.
{
/* Every frame - time in the game times the assigned scroll speed
and never exceed the length of the tile that we assign */
float newPosition = Mathf.Repeat (Time.time * scrollSpeed, tileSizeZAxis);
// New position equal to the start position plus vector3forward times new position
transform.position = startPosition + Vector3.forward * newPosition; // was vector3.forward
scrollSpeed += Time.deltaTime * acceleration; // This makes the plane increase in speed over time with
// whatever our acceleration is set to.
//Play Animation
animation.PlayQueued("Crawling", QueueMode.CompleteOthers);
}
}
} }
I solved the problem!!
This is my script:
using UnityEngine;
using System.Collections;
public class Crawling : MonoBehaviour {
public Animator animator;
private GameManagerScript GMS;
void Start ()
{
animator = GetComponent<Animator> ();
GMS = GameObject.Find ("GameManager").GetComponent<GameManagerScript> ();
}
void Update ()
{
if (GMS.countDownDone == true) {
animator.enabled = true;
}
else
{
animator.enabled = false;
}
}
}
All I do is just enable the Animator that I attach in the Inspector whenever "countDownDone" becomes "true", and for added safety, I added the "else" for it to be disabled. If someone notices a way for me to improve this script, please let me know. Thank you :)
Shorter version:
if (GMS.countDownDone)
animator.enabled = true;
else
animator.enabled = false;
Currently I'm making my first video game using unity written in C#, but now I'm facing a trouble and I don't know what to do anymore. First I already made my character move but, I want to put all the attributes in one script, like it's moveSpeed, attackSpeed, etc. but once I access it to another script, my character just stands and do nothing. Here's my code
public class ClickToMove : MonoBehaviour {
public CharacterController controller; // Use to move the player
private Vector3 position; // Store the position at which the player clicked;
public Attributes attribute;
// Animation variables
public AnimationClip idleClip;
public AnimationClip runClip;
// Use this for initialization
void Start () {
position = transform.position; // Set the position to player's current position
}
// Update is called once per frame
void Update () {
// Execute the code below once the player press left click
if(Input.GetMouseButton(0)) {
locatePosition();
}
moveToPosition();
}
// Locate at which the player clicked on the terrain
private void locatePosition() {
RaycastHit hit; // Get information from ray
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); // A line in 3D space
// Cast a ray that start from the camera with a distance of 1000
if(Physics.Raycast(ray, out hit, 1000)) {
// Store the position if the casted ray is not pointing to the player or enemy's position
if(hit.collider.tag != "Player" && hit.collider.tag != "Enemy") {
position = new Vector3(hit.point.x, hit.point.y, hit.point.z);
}
}
}
// Move to the located position
private void moveToPosition() {
// Check if the player position and the destination is greater than one
if(Vector3.Distance(transform.position, position) > 1) {
// Subtract the clicked position to the player position
Quaternion newRotation = Quaternion.LookRotation (position - transform.position);
// Disable the rotation from x and z angle
newRotation.x = 0f;
newRotation.z = 0f;
// Rotate the player to a new rotation then move forward
transform.rotation = Quaternion.Slerp(transform.rotation, newRotation, Time.deltaTime * attribute.moveSpeed);
controller.SimpleMove(transform.forward * attribute.moveSpeed);
animation.CrossFade(runClip.name);
} else {
animation.CrossFade(idleClip.name);
}
}
}
and here is the script I'm accessing
public class Attributes : MonoBehaviour {
public float moveSpeed;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
}
I don't know what to do anymore. Any help will be appreciated. Thank you.
There are a couple of things that you need to double check, the first being that you are actually assigning a value to attribute.moveSpeed in the inspector. Otherwise moveSpeed will always be 0 and therefore all of your multiplication operations will result in 0, resulting in no movement at all.
Second, make sure that something is actually being hit by your raycast. You can do this a couple ways, first by printing a message to the console saying that you hit something
// Cast a ray that start from the camera with a distance of 1000
if(Physics.Raycast(ray, out hit, 1000)) {
// Send a debug message to Unity's console
Debug.Log("I hit Something!");
// Store the position if the casted ray is not pointing to the player or enemy's position
if(hit.collider.tag != "Player" && hit.collider.tag != "Enemy") {
position = new Vector3(hit.point.x, hit.point.y, hit.point.z);
}
}
and second by checking that the position variable changed values in the inspector. (Make the position variable public and look at your character's inspector)
Note that you've assigned a value to your attribute variable in the inspector because you're not getting NullReferenceExceptions