I have this code here ,i am trying to move an object from one position to another ,on mouse click ,but everytime i run it it justs instantiate the projectile object in a specific position while it had to instantiate it in a the ffor object position
using UnityEngine;
using System.Collections;
public class Shoot : MonoBehaviour
{
public GameObject projectile;
public GameObject foot;
public GameObject mouse;
void Start()
{
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
Vector2 Target = Input.mousePosition;
Vector2 pos1 = Camera.main.ScreenToWorldPoint(Target);
GameObject newObj = (GameObject)GameObject.Instantiate(projectile);
Vector2 pos2 = foot.transform.position;
transform.position=Vector2.Lerp(pos2,pos1,Time.deltaTime);
}
}
There are several issues here, I'll address them one at a time. First, the code for moving the projectile is wired up wrong:
transform.position=Vector2.Lerp(pos2,pos1,Time.deltaTime);
You are moving the object that "Shoot" is attached to, not the new game object (newObj, as you have named it.)
Second, it's important to understand the Update pattern and how to properly use it. Update is run every frame. Time.deltaTime is how much time has passed between the last frame render and this one. This number is usually very small. Lastly, Input.GetMouseButtonDown is true only on the first frame that the mouse is pressed.
The current code you have only attempts to (but fails, due to other code problems) move the projectile the one frame the mouse is clicked. What we want is the mouse click to spawn a projectile, and the projectile to move forward EVERY update.
This will be best accomplished with two classes. I will call them Gun and SeekerBullet. The Gun class will be responsible for creating a bullet every time the mouse button is pressed. The SeekerBullet class will be responsible for moving the bullet to it's target.
Gun
public SeekerBullet ProjectilePrefab;
void Update()
{
if (Input.GetMouseButton(0))
{
Vector2 target = Camera.main.ScreenToWorldPoint(Input.mousePosition);
FireBullet(target);
}
}
void FireBullet(Vector2 target)
{
GameObject projectile = (GameObject)GameObject.Instantiate(ProjectilePrefab, transform.position, Quaternion.Identity);
projectile.GetComponent<SeekerBullet>().Target = target;
}
SeekerBullet
public float MoveSpeed = 5;
public Vector2 Target { get; set; }
public void Update()
{
transform.position = Vector3.MoveTowards(transform.position, Target, MoveSpeed * Time.deltaTime);
if (transform.position == Target)
OnReachTarget();
}
void OnReachTarget()
{
// Do whatever you want here
Destroy(gameObject); // delete this seekerbullet
}
The main Idea I'm trying to stress is isolating your code to perform it's one function well. The gun shouldn't be responsible for moving the projectile, just creating it and telling it where to go.
Also, note that the projectile is starting wherever the Gun is. See this line
GameObject projectile = (GameObject)GameObject.Instantiate(ProjectilePrefab, transform.position, Quaternion.Identity);
The transform.position is the position of the gun object. If you want it to start at the foot, like you have in your code, you can re-implement that just like you have in your example.
Why don't you use Raycast. It's pretty simple. Here's an example (Moves the object (transform) in the direction and distance of translation.):
var obj:Transform;
var hit:RaycastHit;
var move: boolean = false;
var moveSpeed:float;
var moveTime:float;
private var startTime:float;
function Update ()
{
if (Input.GetButtonDown ("Fire1"))
{
var ray = Camera.main.ScreenPointToRay (Input.mousePosition);
if (Physics.Raycast (ray, hit, 10000)){
move = true;
startTime = Time.time;
}
}
if(move){
var curTime = Time.time;
var elapsedTime = curTime - startTime;
var amountToMove = elapsedTime / moveTime;
print(amountToMove);
obj.transform.position = Vector3.Lerp(obj.transform.position, hit.point, amountToMove);
if(obj.transform.position == hit.point){
move = false;
}
This code creates a plane, and a cube. Assign the above code to the plane, assign the cube to the obj var in the inspector. Click on the plane, and watch the fun.
This is pretty simple. You can use the Vector3.Lerp function to achieve this. Use raycasting to get the mouse click position or the touch position. Then use the initial and the final position in the lerp function. The initial position being the position that the gameobject is at now and the final position being the click / touch position.
You can find the article by The Game Contriver on the same here
Move to Touch / Click Position - The Game Contriver
Related
Following this question How to align sprites of smaller sizes to a moving gameobject sprite?, I'm trying to redraw my sprites to avoid rendering transparent pixels and try to maximize performance, and as I try starting this process with the sword sprites, I need to change the way the Sword game object follows the Player, which before was handled simply by using transform.position = hero.transform.position; because since both objects use sprites that are perfect squares they would have the same pivot point despite their size difference.
At first, it seemed to me the problem lied in how the pivots are different, thus I would need to update the transform position of the Sword every time its attack animation changes sprites. For that, I made a function which updates variables that influence the position as that gets updated:
here, heroTransform is a variable which gets sent the Player's transform property on the script's Start function as heroTransform = hero.transform;, where hero is defined right above it as hero = GameObject.Find("Hero");.
I would have expected this to make the Sword which is equipped with this script, called WieldablePosition to follow the player position, but it seems stuck in the same position as it starts:
I'm not sure, but I don't think I changed anything that would stop the Sword from moving. In what cases could a GameObject remain in a single place even as the transform.position is modified? For reference, please view the script:
using UnityEngine;
public class WieldablePosition : MonoBehaviour {
GameObject hero;
HeroMovement heroMovementScript;
private Transform heroTransform;
protected Animator anim;
private bool isAirAttackSingle;
private bool isFacingLeft;
private float x = 0;
private float y = 0;
void Start() {
hero = GameObject.Find("Hero");
heroTransform = hero.transform;
heroMovementScript = hero.GetComponent<HeroMovement>();
anim = GetComponent<Animator>();
isAirAttackSingle = heroMovementScript.isAirAttackSingle;
isFacingLeft = heroMovementScript.isFacingLeft;
}
void Update() {
UpdatePosition();
isAirAttackSingle = heroMovementScript.isAirAttackSingle;
isFacingLeft = heroMovementScript.isFacingLeft;
anim.SetBool("isAirAttackSingle", isAirAttackSingle);
if (isFacingLeft) {
transform.localScale = new Vector3(-1, 1, 1);
} else {
transform.localScale = Vector3.one;
}
}
public void SetPosition(AnimationEvent eventParams) {
string[] eventPositions = eventParams.stringParameter.Split(',');
x = float.Parse(eventPositions[0]);
y = float.Parse(eventPositions[1]);
}
private void UpdatePosition() {
transform.position = new Vector2(heroTransform.position.x + x, heroTransform.position.y + y);
}
}
Vector2 mousePos = Input.mousePosition;
// motion core
if (GameObject.Find("Camera").GetComponent<room>().playerNum == 1)
{
if (Input.GetMouseButtonDown(0))
{
// move script not working
}
}
So, I have mostly every possible solution I could find but none of them worked. I cannot get it to smoothly move with AddForce because I could not figure out a working algorithm to make the force move toward the MousePosition.
The position you're getting out of Input.mousePosition are the coordinates on the screen, not the position in the world. To transform between the two, you can use Camera.ScreenToWorldPoint().
if (Input.GetMouseButtonDown(0))
{
Vector3 mousePos = Input.mousePosition
mousePos = new Vector3(mousePos.x, mousePos.y, Camera.main.nearClipPlane)
Vector3 worldPos = Camera.main.ScreenToWorldPoint(new Vector3())
// move script
}
You might need to edit the z coordinate in the mousePos to the current transform.position.z of the object you're trying to move, or another value that makes most sense here. It acts as a kind of wall, where it'll create the point exactly that far from the camera on your mouse position. This should be a lot cheaper than raycasting, and still works if there's nothing to hit where you're clicking.
I have a script that did this same movement to the handle of a wrecking ball, but I don't have the code with me at the moment. I can't remember exactly how it worked, but I think the idea was that when the mouse was clicked, drag would be set to a very high number and gravity would be set to 0. Then an extremely strong force would be added to counter the drag so that the object would fly towards the mouse without orbiting. When the mouse was released, the drag and gravity would be set back to normal.
I can't test this at the moment because I'm on a chromebook and my PC with Unity on it is in another building, but this code should do the trick if I don't make any errors.
using UnityEngine;
public class ExampleClass : MonoBehaviour
{
float prevDrag, prevGrav;
bool mousedown;
Plane plane;
Rigidbody2D r;
void Start()
{
r = GetComponent<Rigidbody2D>(); // assuming this script is attached to the object being moved.
plane = new Plane(Vector3.up, Vector3.zero);
}
void Update()
{
if(mousedown)
{
float enter;
if (plane.Raycast(ray, out enter))
{
var hitPoint = ray.GetPoint(enter);
var mouseDir = hitPoint - gameObject.transform.position;
rb.AddForce(mouseDir * 9999999);
}
}
}
void OnMouseDown()
{
mousedown = true;
prevDrag = r.drag;
prevGrav = r.gravity;
r.drag = 99999;
r.gravity = 0;
}
void OnMouseUp()
{
mousedown = false;
r.drag = prevDrag;
r.gravity = prevGrav;
}
}
I have a GameObject with a RobotMovment script on it that looks like this:
public class RobotMovement : MonoBehaviour {
private Transform target;
private float speed = 2f;
void Start() {
target = GameObject.FindGameObjectWithTag("Ball").transform;
}
void Update() {
float step = speed * Time.deltaTime;
transform.position = Vector3.MoveTowards(transform.position, target.position, step);
}
}
I then have another script placed on a empty game object called Ball that looks like this:
public class Ball : MonoBehaviour {
public GameObject ballPrefab;
void Start() {
var randomPosition = new Vector3(Random.Range(GetComponent<Collider>().bounds.min.x, GetComponent<Collider>().bounds.max.x), 0.25f, Random.Range(GetComponent<Collider>().bounds.min.z, GetComponent<Collider>().bounds.max.z));
Instantiate(ballPrefab, randomPosition, Quaternion.identity);
}
}
What I want to achieve is to have the RobotMovement gameobject move towards the ball prefab that gets spawned on the start of the game. Now I get a: nullreferenceexception object reference not set error on start of the game. It might be that the the ball prefabs position can't be read by the RobotMovement script at start?
The Awake() function executes before Start(). It's a good idea to use Awake() to find and assign references such as target, then use Start() for whatever initialisation logic you need once you're confident all your references are in place.
That said, your null reference could be in a number of places, but most likely:
No gameobject called "Ball" is found when RobotMovement calls Start().
Ball gets destroyed or the reference is lost at some point, and RobotMovement.Update() throws.
No collider component on Ball, so Ball.Start() throws.
One thing you didn't ask for, but is important to know, is that GetComponent() is an expensive method to invoke, so you'd be better off calling it just once:
Collider collider = GetComponent<Collider>()
var randomPosition = new Vector3(Random.Range(collider.bounds.min.x, collider.bounds.max.x), 0.25f, Random.Range(collider.bounds.min.z, collider.bounds.max.z));
I would like to calculate a new position based on the pitch of a mesh in order to make an object following the top of my object which is rotated:
And result in:
I cannot make the square object as represented above as a child (in the Unity object hierarchy) of the line object because the rotated object can see its scale changed at anytime.
Does a mathematics solution can be used in this case?
Hotspots
If you'd like to place something at a particular location on a generic object which can be scaled or transformed anywhere, then a "hotspot" can be particularly useful.
What's a hotspot?
Edit the target gameobject (the line in this case) and add an empty gameobject to it. Give it some appropriate name - "cross arms hotspot" for example, and then move it to the location where you'd like your other gameobject to target. Essentially, a hotspot is just an empty gameobject - a placement marker of sorts.
How do I use it?
All you need is a reference to the hotspot gameobject. You could do this by adding a little script to the pole gameobject which tracks it for you:
public class PowerPole : MonoBehaviour {
public GameObject CrossArmsHotspot; // Set this in the inspector
}
Then you can get that hotspot reference from any power pole instance like this:
var targetHotspot = aPowerPoleGameObject.GetComponent<PowerPole>().CrossArmsHotspot;
Then it's just a case of getting your target object to place itself where that hotspot is, using whichever technique you prefer. If you want it to just "stick" there, then:
void Start(){
targetHotspot = aPowerPoleGameObject.GetComponent<PowerPole>().CrossArmsHotspot;
}
void Update(){
transform.position = targetHotspot.transform.position;
}
would be a (simplfied) example.
A more advanced example using lerp to move towards the hotspot:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CrossArmsMover : MonoBehaviour
{
public GameObject PowerPole;
private GameObject targetHotspot;
public GameObject CrossArms;
public float TimeToTake = 5f;
private float timeSoFar;
private Vector3 startPosition;
private Quaternion startRotation;
// Start is called before the first frame update
void Start()
{
startPosition = CrossArms.transform.position;
startRotation = CrossArms.transform.rotation;
targetHotspot = PowerPole.GetComponent<PowerPole>().CrossArmsHotspot;
}
// Update is called once per frame
void Update()
{
timeSoFar+=Time.deltaTime;
var progress = timeSoFar/TimeToTake;
// Clamp it so it doesn't go above 1.
if(progress > 1f){
progress = 1f;
}
// Target position / rotation is..
var targetPosition = targetHotspot.transform.position;
var targetRotation = targetHotspot.transform.rotation;
// Lerp towards that target transform:
CrossArms.transform.position = Vector3.Lerp(startPosition, targetPosition, progress);
CrossArms.transform.rotation = Quaternion.Lerp(startRotation, targetRotation, progress);
}
}
You would need to put a script on the following gameobject in wich you would put :
GameObject pitcher = //reference to the gameobject with the pitch;
const int DISTANCE_ON_LINE = //distance between the 2 objects
void Update() {
transform.position = pitcher.transform.position + pitcher.transform.forward * DISTANCE_ON_LINE;
}
Hi there I'd like to be able to click on an object then click a position on a plane and would like my object to lerp to that mouse clicked position. The problem is doing this for more than one object becomes tricky. Anybody got any ideas? so far I have followed the tutorial on Coroutines on unity3D's website under the advanced scripting tutorials. here is the code:
attached to the game object:
private Vector3 target;
public float smoothing = 7f;
public Vector3 Target
{
get{return target;}
set
{
target = value;
StopCoroutine("Movement");
StartCoroutine("Movement",target);
}
}
IEnumerator Movement(Vector3 target)
{
while (Vector3.Distance(transform.position,target) > 0.05f)
{
transform.position = Vector3.Lerp(transform.position, target, smoothing* Time.deltaTime);
yield return null;
}
}
attached to the plane:
public propertiesandcoroutines coroutinescript;
private float Deltapos = 0.5f;
private GameObject collobj;
public void OnMouseDown()
{
Ray ray = Camera.main.ScreenPointToRay (Input.mousePosition);
RaycastHit hit;
Physics.Raycast (ray, out hit);
if (hit.collider.gameObject == gameObject)
{
coroutinescript.Target = new Vector3( hit.point.x,hit.point.y + Deltapos,hit.point.z);
}
}
this code works perfectly for one game object. how can I change this to work for a game object that I click on?
You just need to add a couple of lines to each script to get the result you want.
To the script attached to your moveable GameObjects
//This is the same type as the script attached to your plane. Rename it
//to whatever it's actually called (I've just called it PlaneScript). Also
//drag the plane into this field for each of the moveable GameObjects in the inspector
public PlaneScript planeScript;
//Paste this method anywhere inside the script attached to the GameObjects
void OnMouseUpAsButton () {
planeScript.SetObjectToMove(this);
}
Now, in the script attached to your plane, add the following
public void SetObjectToMove (propertiesandcoroutines script) {
coroutinescript = script;
}
How it works
Basically, what we're doing here is when one of your GameObjects are clicked, we reassign the "coroutinescript" variable in the script attached to the plane.
So, when the plane is clicked, the last GameObject that was clicked on before clicking the plane is moved.