I have a GameObject, let's call it editor, that follows the position of the mouse in the world using the below script:
using UnityEngine;
public class FollowCursor : MonoBehaviour
{
void Update()
{
Plane objPlane = new Plane(-Camera.main.transform.forward,
gameObject.transform.position);
Ray mouseRay = Camera.main.ScreenPointToRay(Input.mousePosition);
if (objPlane.Raycast(mouseRay, out float distance))
{
gameObject.transform.position = mouseRay.GetPoint(distance);
}
}
}
This editor object is used in many places, one of which is to control panning of the camera. I control this panning using the script below, which is attached to the main camera:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraControls : MonoBehaviour
{
FollowCursor editor;
Vector2 camPosOnClick;
Vector2 mousePosOnClick;
public Vector2 RelativeToCamera(Vector2 worldPoint)
{
return worldPoint - (Vector2)gameObject.transform.position;
}
public Vector2 RelativeToWorld(Vector2 camPoint)
{
return camPoint + (Vector2)gameObject.transform.position;
}
void Start()
{
editor = GameObject.FindWithTag("GameController")
.GetComponent<FollowCursor>();
}
void Update()
{
if (Input.GetMouseButtonDown(1))
{
camPosOnClick = gameObject.transform.position;
mousePosOnClick =
RelativeToCamera(editor.gameObject.transform.position);
}
if (Input.GetMouseButton(1))
{
Vector2 change = mousePosOnClick
- RelativeToCamera(editor.gameObject.transform.position);
gameObject.transform.position = camPosOnClick + change;
}
}
}
The intended behavior of this code is upon right clicking, the position of the editor (tracking the mouse position) and the camera are saved. Each frame, the distance between the current editor position and the saved position (which equates to the drag distance) is added to the camera's saved position.
I can see no reason this code shouldn't work, but nothing happens when I right click. The action of right clicking causes the editor object to stop tracking its position; the raycast it uses fails every frame after right-clicking. I cannot figure out what right-clicking is doing that affects this raycast, nor how to fix it. Is there a flaw in this code that I have missed?
Related
Hi!
After the discussion with Ruzihm in the comments. I've now created a simple version of my game to better ask the question I'm having.
The question now is, since I'm not able to manually create a connection to the testObject field in the inspector. How do I now tell Unity to use my instantiated objects while the game is running?
And is this a good solution for a RTS game that may have 100s of Units active at a time? The end goal here is to apply this force to a radius around the cursor. Which I was thinking of using Physics.OverlapSphere
Here's the minimal scenario of what I have:
New Unity scene
Attached the InputManager to the main camera.
Created a capsule and a plane.
Added ApplyForce to the Capsule
Created a prefab from the capsule and deleted it from the scene.
In the InputManager I added the ability to press space to Instantiate a capsule with the ApplyForce script attached..
Drag the capsule prefab to the InputManager "objectToGenerate"
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace GL.RTS.Mites
{
public class InputManager : MonoBehaviour
{
public GameObject testObject;
public ApplyForce onSpawnTest;
public GameObject objectToGenerate;
void Start()
{
onSpawnTest = testObject.GetComponent<ApplyForce>();
}
void Update()
{
if(Input.GetKeyDown(KeyCode.Space))
{
Instantiate(objectToGenerate);
}
if (Input.GetMouseButton(0))
{
onSpawnTest.PushForward();
}
}
}
}
The ApplyForce script that I attach to the Capsule:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace GL.RTS.Mites
{
public class ApplyForce : MonoBehaviour
{
public float moveSpeed;
Rigidbody rb;
void Start()
{
rb = GetComponent<Rigidbody>();
Debug.Log("A Mite has spawned!");
}
public void PushForward()
{
rb.AddRelativeForce(Vector3.up * moveSpeed * Time.deltaTime);
Debug.Log("A force of: " + moveSpeed + " is being added.");
}
}
}
Well, you are creating your new instances of your object, but your input manager immediately forgets about them (note that you do nothing with the return value). The InputManager only knows about the ApplyForce that was created in its Start (and then interacts with it depending on mouse input) and your ApplyForce script knows nothing about any InputManager. So, it should come as no surprise that only the first instance reacts to the mouse input.
So, something has to be done to your InputManager and/or your ApplyForce. Your InputManager could remember the instances it creates (which isn't enough, because what if for example, a map trigger creates new player controllable units) or it could go looking for units each time.
Your ApplyForce could register with the InputManager when they are created, but then you would need to loop through the units and find out which ones are under the mouse, anyway.
Since you only want to select ones based on what is near or under your cursor and only when input occurs and not like every frame, I would go with the simplest approach, just letting your InputManager find the units when it needs them. Something like below, explanation in comments:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace GL.RTS.Mites
{
public class InputManager : MonoBehaviour
{
public GameObject testObject;
public ApplyForce onSpawnTest;
public GameObject objectToGenerate;
private Camera mainCam;
// which layers to consider for cursor detection
[SerializeField] LayerMask cursorLayerMask;
// how big for cursor detection
[SerializeField] float cursorRadius;
void Awake()
{
// cache main camera
mainCam = Camera.main;
}
void Update()
{
if(Input.GetKeyDown(KeyCode.Space))
{
Instantiate(objectToGenerate);
}
if (Input.GetMouseButton(0))
{
Collider[] colls = FindCollidersUnderCursor();
// check each collider for an applyforce and use it if present
foreach( Collider coll in colls)
{
ApplyForce af = coll.GetComponent<ApplyForce>();
if (af != null)
{
af.PushForward();
}
}
}
}
Collider[] FindCollidersUnderCursor()
{
// find ray represented by cursor position on screen
// and find where it intersects with ground
// This technique is great for if your camera can change
// angle or distance from the playing field.
// It uses mathematical rays and plane, no physics
// calculations needed for this step. Very performant.
Ray cursorRay = mainCam.ScreenPointToRay(Input.mousePosition);
Plane groundPlane = new Plane(Vector3.up, Vector3.zero);
if (groundPlane.Raycast(cursorRay, out float cursorDist))
{
Vector3 worldPos = cursorRay.GetPoint(cursorDist);
// Check for triggers inside sphere that match layer mask
return Physics.OverlapSphere(worldPos, cursorRadius,
cursorLayerMask.value, QueryTriggerInteraction.Collide);
}
// if doesn't intersect with ground, return nothing
return new Collider[0];
}
}
}
Of course, this will require that every unit you're interested in manipulating has a trigger collider.
I am new in Unity and trying to create an Android mobile game.
First, I decided to create a script called "SelectObject" that will move the objects, provided that the object has a collider.
I add this script to the "MainCamera" components, since we need to determine if the camera ray touched the object.
Here is the script "SelectObject":
using UnityEngine;
using System.Collections;
public class SelectObject : MonoBehaviour
{
void Start() {
}
public void Update()
{
if ((Input.touchCount > 0) && (Input.touches[0].phase == TouchPhase.Began)) {
Ray ray = Camera.main.ScreenPointToRay(Input.touches[0].position);
RaycastHit hit; //Declaring the variable hit, in order to further determine if the camera's ray touhed the object
if (Physics.Raycast(ray, out hit)) {
if (hit.collider != null) { //If the ray of camera touches the collider
hit.collider.GetComponent<Move>().Movement(); //Calling the "Movement" method from script "Move" to move the object
}
}
}
}
}
The Move class is a component of the "Cylinder" object that should move when you click on it.
Here is the "Move" class:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Move : MonoBehaviour {
public float speedModifier; //Determine the speed of movement
public Touch touch; //Using this variable, we will determine if there was at least some touch on the screen
void Start() {
speedModifier = 2.01f;
}
public void Update() {
}
public void Movement() { //Creating a "Movement" method to call it in the "SelectObject" script
touch = Input.GetTouch(0);
if(touch.phase == TouchPhase.Moved) {
transform.position = new Vector3(transform.position.x + touch.deltaPosition.x * speedModifier, transform.position.y, transform.position.z + touch.deltaPosition.y * speedModifier);
}
}
}
But when I start the game, the cylinder still doesn't move.
What am I doing wrong?
Not 100% clear on what you're trying to do but have you tried passing your location into the Movement() method? Something like this.
public void Movement(Vector3 targetLocation) {
transform.position = targetLocation;
}
Then you'd call it like this.
Movement(Input.touches[0].position);
Just a note since I noticed your speed modifier. The way your code is currently set up is going to instanly move the object to the touch location. If you would like to have it smoothly move to it at a specified speed you'll need to look up Lerping or store the previous position, current, and target position.
Sorry for a vague title, I'm trying to keep it short.
Anyway, I've been attempting to create a 3D grabbing script that involves the mouse. For some reason, the GameObject gets stuck in the same position no matter when I grab the ball or where I move my cursor.
Here is the code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Grab: MonoBehaviour
{
public bool grabby;
void OnMouseDown()
{
grabby = true;
}
void OnMouseUp()
{
grabby = false;
}
// Update is called once per frame
void Update()
{
if (grabby)
{
transform.position = Camera.main.ScreenToWorldPoint(Input.mousePosition) + Camera.main.transform.forward * 10f;
}
}
}
I tried breakpoints, which does nothing. The variable grabby is also changing, so that's not the problem. The only other issue I can think of is my math. If it helps, the Grab script is placed on the object you are supposed to grab.
In 3D world, the computer doesn't recognize the mouse cursor where you think it is. To solve this, you can use the function ScreenPointToRay. for example:
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hitInfo))
{
// Do something...
Debug.Log(hitInfo.point + " " + hitInfo.collider.name);
}
hitInfo.point Will give you the point where the mouse cursor touches.
hitInfo.collider.name Will give the name of the object that the mouse cursor touches.
I want to move my player around in my level without a NavMesh Agent.
I was thinking something about raycasting, but i have tried everything and even looked up videos and searched on google about it, can't seem to find anything that works
Plz help!
you can use this for moving to mouseclick position.
This method will move object or player to the position where mouse is clicked on screen.
sorry for syntax error i have typed directly here
using UnityEngine;
using System.Collections;
public class MouseMovement : MonoBehaviour
{
public float speed = 10f;
bool isplayermove = false;
// Use this for initialization
private void Start ()
{
Vector3 playerpos = transform.position;
}
// Update is called once per frame
private void Update ()
{
if (Input.GetKeyDown(KeyCode.Mouse0))
{
is moveplayer = true;
}
if(ismoveplayer)
{
Vector3 mousePos = Camera.main.ScreenToWorldPoint(new Vector3(input.mousepos.x,input.mousepos.y,input.mousepos.z);
transform.position = Vector3.MoveTowards(transform.position,new Vector3(mousepos.x,transform.position.y,mousepos.z),speed * time.deltatime);
if(transform.position == mousepos)
{
ismoveplayer = false;
}
}
}
}
The droid have a box collider also the door have a box collider.
But when i move the character(FPSController/FirstPersoncharacter) the player the character stop can't move through the door but the droid can.
I tried to turn off/on the Is Trigger property on the droid box collider but it didn't change.
I want the droid to act like the player when colliding with other objects like the droid is part of the player.
The only code i'm using is attached to the FirstPersonCharacter:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DroidMove : MonoBehaviour
{
public GameObject droid;
private float distance;
private Camera cam;
private void Start()
{
cam = GetComponent<Camera>();
distance = Vector3.Distance(cam.transform.position, droid.transform.position);
droid.SetActive(false);
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.F))
{
droid.SetActive(!droid.activeInHierarchy);
}
}
}
I tried to add a Rigidbody component to the droid to the NAVI or to the Droid_Player but it didn't solve it. I don't have any other code that handle collidings.
UPDATE to what i tried and did so far:
From the NAVI (Droid) i removed the box collider component and added two things:
Character Controller component
Control script (This script is coming with the NAVI(Droid))
This is the Control script:
using UnityEngine;
using System.Collections;
public class Control : MonoBehaviour
{
public float rotationDamping = 20f;
public float speed = 10f;
public int gravity = 0;
public Animator animator;
float verticalVel; // Used for continuing momentum while in air
CharacterController controller;
void Start()
{
controller = (CharacterController)GetComponent(typeof(CharacterController));
}
float UpdateMovement()
{
// Movement
float x = Input.GetAxis("Horizontal");
float z = Input.GetAxis("Vertical");
Vector3 inputVec = new Vector3(x, 0, z);
inputVec *= speed;
controller.Move((inputVec + Vector3.up * -gravity + new Vector3(0, verticalVel, 0)) * Time.deltaTime);
// Rotation
if (inputVec != Vector3.zero)
transform.rotation = Quaternion.Slerp(transform.rotation,
Quaternion.LookRotation(inputVec),
Time.deltaTime * rotationDamping);
return inputVec.magnitude;
}
void AnimationControl ()
{
if(Input.GetKey("a") || Input.GetKey("s") || Input.GetKey("d") || Input.GetKey("w"))
{
animator.SetBool ("Moving" , true);
}
else
{
animator.SetBool ("Moving" , false);
}
if(Input.GetKey("space"))
{
animator.SetBool ("Angry" , true);
}
else
{
animator.SetBool ("Angry" , false);
}
if(Input.GetKey("mouse 0"))
{
animator.SetBool ("Scared" , true);
}
else
{
animator.SetBool ("Scared" , false);
}
}
void Update()
{
UpdateMovement();
AnimationControl();
if ( controller.isGrounded )
verticalVel = 0f;// Remove any persistent velocity after landing
}
}
Now when i move the character close to a door or wall also the NAVI(Droid) stop and is not moving through the door or wall and this is fine. But now i have another problem. If i will keep moving to the door or wall it looks like the character is moving over/on the NAVI Droid.
The Navi droid is not changing position.
In this screenshot is how it looks like when i moved close to the door the collider on the door and the NAVI Droid are working and the navi droid can't move through the door:
In this screenshot you can see what is happened when i kept moving the character to the door. On the top screen view the NAVI droid is in the same position but on the Game View on the bottom it seems like the character is moving over/on the NAVI droid.
And if i will keep moving to the door the character will not move but the droid looks like pushed back or the character will keep moving over the droid.
This is a short video clip i recorded showing the problem.
The problem start at second 20:
Collider problem
I tried to add box collider to the droid tried rigidbody nothing worked only this script and component but now i have this problem in the video.
Looks like your droid probably needs a RigidBody component.
If that doesn't solve it, you should add some relevant code snippets to your question to show how/where you've tried to detect and handle collisions.