Unet Spawnable prefab not following Player Prefab on Client Side - unity3d

I have got a problem with Gun not sticking to a Player. It only happens for a client. As you can see on the screen, the gun position is fine for the host(Player on the right). Gun Prefab has Network Identity with Local Player Authority checked, and Network Transform, same for Player.
This is my code for Player:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
public class Player : NetworkBehaviour
{
[SerializeField] float speed = 10f;
[HideInInspector] public GameObject playerGun;
public GameObject gunPrefab;
void Update()
{
if (!isLocalPlayer)
{
return;
}
Movement();
if (Input.GetKeyDown(KeyCode.I))
{
CmdGetGun();
}
if (playerGun)
CarryGun();
}
private void Movement()
{
Vector3 position = new Vector3(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical")).normalized * Time.deltaTime * speed;
transform.position += position;
MouseMovement();
}
private void MouseMovement()
{
Vector3 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition) - transform.position;
mousePosition.Normalize();
float rotation_z = Mathf.Atan2(mousePosition.y, mousePosition.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.Euler(0f, 0f, rotation_z);
}
[Command]
public void CmdGetGun()
{
Debug.Log("SPAWNING A GUN");
playerGun = (GameObject)Instantiate(gunPrefab, transform.position, transform.rotation);
NetworkServer.SpawnWithClientAuthority(playerGun, connectionToClient);
}
public void CarryGun()
{
Debug.Log("carring A GUN");
playerGun.transform.position = new Vector3(transform.position.x, transform.position.y, transform.position.z - 1);
playerGun.transform.rotation = transform.rotation;
}
}
I spent days trying to figure it out. I am new to Unity, especially Unet and maybe i do not understand something.
I know the position of a gun is wrong but i will change it after i deal with this problem. For now i just want it to stick to a Player both on Client and Host side.

Problem
This Command method is always executed on the server .. so also with the properties on the server.
Meaning:
You never set playerGun on the client side only on the server in
playerGun = Instantiate ...
So, since your client never gets its playerGun value set CarryGun is never executed on the client.
Solution 1
To avoid that you should use a ClientRpc method to set the value also on all clients.
[Command]
public void CmdGetGun()
{
Debug.Log("SPAWNING A GUN");
playerGun = (GameObject)Instantiate(gunPrefab, transform.position, transform.rotation);
NetworkServer.SpawnWithClientAuthority(playerGun, connectionToClient);
// Tell the clients to set the playerGun reference.
// You can pass it as GameObject since it has a NetworkIdentity
RpcSetGunOnClients(playerGun);
}
// Executed on ALL clients
// (which does not mean on all components but just the one of
// this synched GameObject, just to be clear)
[ClientRpc]
private void RpcSetGunOnClients (GameObject gun)
{
playerGun = gun;
}
Solution 2
This is an alternative solution which does basically the same steps from above but it would not longer need the CarryGun method and a NetworkTransform, making your code more efficient by saving both, method calls and network bandwidth:
Instead of spawning the gun on top level in hierarchy to a certain global position and rotation
playerGun = (GameObject)Instantiate(gunPrefab, transform.position, transform.rotation);
and than updating it's position and rotation all the time to the player's tranfroms and transfere them separately via the NetworkTransforms you could simply make it a child of the Player object itself using e.g. Instantiate(Object original, Vector3 position, Quaternion rotation, Transform parent); on the server:
playerGun = (GameObject)Instantiate(gunPrefab, gunLocalPositionOffset, gunLocalRotationOffset, transform);
This should allways keep it in the correct position without having to Update and synchronize it's position and rotation all the time and without any further transfere methods and values.
All you have to do is again have a ClientRpc in order to tell the clients as well to make this gun a a child of your Player using e.g. SetParent(Trasnform parent, bool worldPositionStays):
playerGun.transform.SetParent(transform, false);
and if necessary apply the local position and rotation offsets. Again you could use a value, everyone has acces to or pass it to the ClientRpc from the server - your choice ;)
so your methods could now look like
// In order to spawn the gun with an offset later
// It is up to you where those values should come from / be passed arround
// If you crate your Prefab "correctly" you don't need them at all
//
// correctly means: the most top GameObject of the prefab has the
// default values position(0,0,0) and rotation (0,0,0) and the gun fits perfect
// when the prefab is a child of the Player => You don't need any offsets at all
Vector3 gunLocalPositionOffset= Vector3.zero;
Quaternion gunLocalRotationOffset= Quaternion.identity;
[Command]
public void CmdGetGun()
{
Debug.Log("SPAWNING A GUN");
// instantiates the prefab as child of this gameObject
// you still can spawn it with a local offset position
// This will make its position be already synched in the Players own
// NetworkTransform -> no need for a second one
playerGun = (GameObject)Instantiate(gunPrefab, gunLocalPositionOffset, gunLocalRotationOffset, gameobject);
// you wouldn't need this anymore since you won't change the spoition manually
// NetworkServer.SpawnWithClientAuthority(playerGun, connectionToClient);
NetworkServer.Spawn(playerGun);
// Tell the clients to set the playerGun reference.
// You can pass it as GameObject since it has a NetworkIdentity
RpcSetGunOnClients(playerGun);
}
// Executed on all clients
[ClientRpc]
private void RpcSetGunOnClients (GameObject gun)
{
// set the reference
playerGun = gun;
// NetworkServer.Spawn or NetworkServer.SpawnWithClientAuthority doesn't apply
// the hierachy so on the Client we also have to make the gun a child of the player manually
// use the flag worldPositionStays to avoid a localPosition offset
playerGun.transform.SetParent(transform, false);
// just to be very sure you also could (re)set the local position and rotation offset later
playerGun.transform.localPosition = gunLocalPositionOffset;
playerGun.transform.localrotation = gunLocalRotationOffset;
}
Update1
For overcoming the problem with later connected clients refer to this answer.
It suggests using a [SyncVar] and override OnStartClient. An adopted version could look like
// Will be synched to the clients
// In our case we know the parent but need the reference to the GunObject
[SyncVar] public NetworkInstanceId playerGunNetId;
[Command]
public void CmdGetGun()
{
Debug.Log("SPAWNING A GUN");
playerGun = (GameObject)Instantiate(gunPrefab, gunLocalPositionOffset, gunLocalRotationOffset, transform);
// Set the playerGunNetId on the Server
// SyncVar will set on all clients including
// newly connected clients
playerGunNetId = playerGun.GetComponent<NetworkIdentity>().netId;
NetworkServer.Spawn(playerGun);
RpcSetGunOnClients(playerGun);
}
public override void OnStartClient()
{
// When we are spawned on the client,
// find the gun object using its ID,
// and set it to be our child
GameObject gunObject = ClientScene.FindLocalObject(playerGunNetId);
gunObject.transform.SetParent(transform, false);
gunObject.transform.localPosition = gunLocalPositionOffset;
gunObject.transform.localrotation = gunLocalRotationOffset;
}
(Note you still need the ClientRpc for the already connected clients.)
Update2
The method from Update1 might not work because playetGunNetId might not be set yet when OnStartPlayer is executed.
To overcome this you can use a hook method for our SyncVar. Something like
// The null reference might somehow still come from
// ClientScene.FindLocalObject
[SyncVar(hook = "OnGunIdChanged"]
private NetworkInstanceId playerGunNetId;
// This method is executed on all clients when the playerGunNetId value changes.
// Works when clients are already connected and also for new connections
private void OnGunIdChanged (NetworkInstanceId newValue)
{
// Honestly I'm never sure if this is needed or not...
// but in worst case it's just redundant
playerGunNetId = newValue;
GameObject gunObject = ClientScene.FindLocalObject(playerGunNetId);
gunObject.transform.SetParent(transform, false);
gunObject.transform.localPosition = gunLocalPositionOffset;
gunObject.transform.localrotation = gunLocalRotationOffset;
}
Now you shouldn't even need the ClientRpc anymore since everything is handled by the hook for both, already connected and newly connected clients.

Related

Object won fall of the platform in Unity

So i made an ball(player) which moves forward on it's own with script. I want to make that ball act like a normal ball. when it riches the edge of platform it won't fall off. Basicaly it stops on the edge. Here's my image:
Here's my controller script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SwerveInputSystem : MonoBehaviour
{
private float _lastFrameFingerPositionX;
private float _moveFactorX;
public float MoveFactorX => _moveFactorX;
void Start(){
}
// Update is called once per frame
void Update()
{
if (Input.GetMouseButtonDown(0))
{
_lastFrameFingerPositionX = Input.mousePosition.x;
}
else if (Input.GetMouseButton(0))
{
_moveFactorX = Input.mousePosition.x - _lastFrameFingerPositionX;
_lastFrameFingerPositionX = Input.mousePosition.x;
}
else if (Input.GetMouseButtonUp(0))
{
_moveFactorX = 0f;
}
}
}
This is Second script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour{
private SwerveInputSystem _swerveInputSystem;
[SerializeField] private float swerveSpeed = 5f;
[SerializeField] private float maxSwerveAmount = 1f;
[SerializeField] private float verticalSpeed;
void Start(){
_swerveInputSystem = GetComponent<SwerveInputSystem>();
}
void Update(){
float swerveAmount = Time.deltaTime * swerveSpeed * _swerveInputSystem.MoveFactorX;
swerveAmount = Mathf.Clamp(swerveAmount, -maxSwerveAmount, maxSwerveAmount);
transform.Translate(swerveAmount, 0, 0);
float verticalDelta = verticalSpeed * Time.deltaTime;
transform.Translate(swerveAmount, verticalDelta, 0.1f);
}
}
EDITED: Adjusted the answer since we now have some source code.
You are positioning the player directly (using its transform) which will mess up the physics. The purpose of a rigidbody is to let Unity calculate forces, gravity, and so on for you. When you are using physics, and you want to move an object you have three main options:
Teleporting the object to a new position, ignoring colliders and forces like gravity. In this case use the rigidbody's position property.
_ourRigidbody.position = new Vector3(x, y, z);
Moving the object to the new position, similar to teleporting but the movement can be interrupted by other colliders. So, if there is a wall between the object and the new position, the movement will be halted at the wall. Use MovePosition().
_ourRigidbody.MovePosition(new Vector3(x, y, z));
Adding some force to the object and letting the physics engine calculate how the object is moved. There are several options like AddForce() and AddExplostionForce(), etc... see the Rigidbody component for more information.
_ourRigidbody.AddRelativeForce(new Vector3(x, y, z));
In your case you can simply remove the transsform.Translate() calls and instead add some force like this:
//transform.Translate(swerveAmount, 0, 0);
//transform.Translate(swerveAmount, verticalDelta, 0.1f);
Vector3 force = new Vector3(swerveAmount, verticalDelta, 0);
_ourRigidbody.AddForce(force);
We can get the _ourRigidbody variable in the Awake() or Start() method as normal. As you can see I like the Assert checks just to be safe, one day someone will remove the rigidbody by mistake, and then it is good to know about it...
private SwerveInputSystem _swerveInputSystem;
private Rigidbody _ourRigidbody;
void Start()
{
_swerveInputSystem = GetComponent<SwerveInputSystem>();
Assert.IsNotNull(_swerveInputSystem);
_ourRigidbody = GetComponent<Rigidbody>();
Assert.IsNotNull(_ourRigidbody);
}
One likely reason your Rigidbody is not being affected by gravity is due to having the field isKinematic checked to on. From the Rigidbody docs, when toggling on isKinematic,
Forces, collisions or joints will not affect the rigidbody anymore.
The rigidbody will be under full control of animation or script
control by changing transform.position
As gravity is a force and no forces act on the object when this setting is checked, your object will not fall when it is no longer on the platform. A simple solution is to uncheck this box.

Trying to stop the momentum when re spawn

I have looked around the web for answers but can't find a working one.
I have tried changing the code and I am pretty sure it's a very easy noob mistake as this is my first game.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class respawn : MonoBehaviour
{
[SerializeField] private Transform player;
[SerializeField] private Transform respawnPoint;
private Rigidbody Rigidbody;
void FixedUpdate()
{
if (player.position == respawnPoint.position);
{
GetComponent<Rigidbody>().velocity = 0;
Rigidbody.angularVelocity = 0;
}
}
private void OnTriggerEnter(Collider other)
{
player.transform.position = respawnPoint.transform.position;
}
}
The very first thing you want to do is to look at the Unity error console. Your code throws multiple errors, they will appear in red. Double-click on an error to be taken to its line in your code.
Also, try get a good editor, like VS Code, as it will help you find issues immediately by adding red wavy underlines. With above two approaches in hand, you will be able to find ~99% of your issues yourself the next time, and then can ask in forums for the remaining 1%.
Let's go through your code step by step:
private Rigidbody Rigidbody;
In Unity you normally want the type in upper case Rigidbody, but the variable name in lowercase rigidbody (even though the C# coding conventions suggest uppercase properties). Either way, don't use the same name as the type, so write:
private Rigidbody rigidbody;
or e.g. (I'm now omitting the default private, though you may also prefer to keep it, and I use a shorter variable name, and clarify its start value):
Rigidbody body = null;
In the following code:
GetComponent<Rigidbody>().velocity = 0;
... you first of all get the rigidbody component again. This isn't needed if you got it once in start (and for repeated usage, can lag), so do it like this:
void Start()
{
rigidbody = GetComponent<Rigidbody>();
}
And secondly, you assign a 0, but you want to assign new Vector3(0f, 0f, 0f), or just
rigidbody.velocity = Vector3.zero;
rigidbody.angularVelocity = Vector3.zero;
Lastly, you write
player.transform.position = respawnPoint.transform.position;
... but player and respawnPoint are already transforms, so you can use the simpler
player.position = respawnPoint.position;
Good luck!
When you enter the Trigger you will be teleported to the respawn position, but you cannot depend on FixedUpdate running on that exact pixel of the respawn position.
The most straigh forward answer is to, well, do the code as you would describe the issue: "When you enter a trigger, set the position to the respawn position and set velocity to 0":
I would change the entire script to:
public class respawn : MonoBehaviour
{
[SerializeField] private Transform player;
[SerializeField] private Transform respawnPoint;
private Rigidbody rigidbody;
void Start()
{
rigidbody = GetComponent<Rigidbody>()
}
private void OnTriggerEnter(Collider other)
{
player.position = respawnPoint.position;
rigidbody.velocity = Vector3.zero;
}
}
and don't forget to populate the player and respawnPoint values in the inspector.
Also, keep in mind that the current code is prone to errors, if you have other objects moving around. Right now, if ANY object enters the Trigger the PLAYER position will be reset. You could fix this with check for player
private void OnTriggerEnter(Collider other)
{
if (other.transform == player)
{
player.position = respawnPoint.position;
rigidbody.velocity = Vector3.zero;
}
}

How to move a gameobject in Unity with the click on UI button when otherwise I would need the Update function to check for new inputs?

I am having a little trouble understanding the following. I am working on a simple game where the player can move a game object around by clicking left or right on the keyboard. Simple enough:
void Update()
{
if (Input.GetKey(KeyCode.LeftArrow))
{
transform.position += Vector3.left * movementSpeed * Time.deltaTime;
}
if (Input.GetKey(KeyCode.RightArrow))
{
transform.position += Vector3.right * movementSpeed * Time.deltaTime;
}
if (Input.GetKey(KeyCode.UpArrow))
{
transform.position += Vector3.up * jumpHeight * Time.deltaTime;
}
if (Input.GetKey(KeyCode.DownArrow))
{
transform.position += Vector3.down * movementSpeed * Time.deltaTime;
}
}
However, I also have two buttons on screen, that are also supposed to move the player just as the keyevents do. What I dont understand is, that I need an update function to check every frame if a new button is pressed, right? So now I have my button and assign a script to it, where I can attach the button to a certain function of that script. But I cannot simply assign the button to a new "update" function that checks for different inputs of that button. How do I get my Ui Button to also controll the player JUST like the Update function does here?
Thank you!
Check out the Screenshot of the Scene. Just create an UI with an EventSystem. Create a button within your UI. Attach an EventTrigger to your button. Now create two Events on your EventTrigger:
(1) Pointer Down (will be called, when the button get's pressed down.)
(2) Pointer Up (will be called when the button is released.)
Attach the script at the end of this answer as a component to your player or cube or whatever you wanna move.
Now you can see on the screenshot, that the public method MoveCube.MoveLeft get's called whenever the MoveLeftButton is pressed. Whenever it is released, the MoveCube.StopMovingLeft gets called. The joke is about the bool, that will be switched from false to true and back. The actual movement happens in the Update, that essentially follows the same logic as the script you have provided. Hope this helps.
private bool moveLeft, moveRight;
void Start ()
{
// Get Rigidbody or CC or whatever you use to move your object.
}
void Update ()
{
if (moveLeft)
{
// Move Left
Debug.Log("Moving Left");
}
}
public void MoveLeft()
{
moveLeft = true;
}
public void StopMovingLeft()
{
moveLeft = false;
}
EDIT: Very important - I just saw that you are using transform.position += Vector3 to move the object. You will have big troubles with accurate collision, as your function is literally teleporting your gameobject to the new position. To avoid bad colliding you should use Rigidbody.AddForce or if you want to use transform you can easily use transform.Translate:
From Unity Docs:
transform.Translate(Vector3.forward * Time.deltaTime);
EDIT 2: Here is the code you have requested in the comment to this answer. Just copy the script, should work fine.
private bool moveLeft, moveRight;
// Create the rigidbody and the collider
private Rigidbody rb;
private Collider col;
// Create a variable you can change in inspector window for speed
[SerializeField]
private float speed;
void Start ()
{
// You can either use a CC (Character Controller) or a Rigidbody. Doesn't make that much of a difference.
// Important thing is, if you use a rigidbody, you will need a collider as well. Collider detect collision
// but rigidbodies do the actual work with regards to physics. In this case we use rigidbody/collider
// Get the rigidbody
// If you are making a 2D game, you should use Rigidbody2D
// The rigidbody will simulate actual physics. I tested this script, the
// rigibody will accelerate and will need time to slow down upon breaking
// You can change it's mass and stuff
rb = GetComponent<Rigidbody>();
// Now in this case we just get any collider. You can be more specific, if you know which collider your gameobjects has
// e.g. BoxCollider or SphereCollider
// If you are making a 2D game, you should use Collider2D
col = GetComponent<Collider>();
}
void Update ()
{
if (moveLeft)
{
// If you make a 2D game, use Vector2
rb.AddForce(Vector3.left * speed);
}
}
public void MoveLeft()
{
moveLeft = true;
}
public void StopMovingLeft()
{
moveLeft = false;
}
You could do something like this. In your script, attached to a button:
using UnityEngine.EventSystems; // Required when using Event data.
public class ExampleClass : MonoBehaviour, IPointerDownHandler // required interface when using the OnPointerDown method.
{
bool moveLeft = false;
// Do this when the mouse is clicked over the selectable object this script is attached to.
public void OnPointerDown(PointerEventData eventData)
{
moveLeft = true;
Debug.Log(this.gameObject.name + " was clicked.");
}
public void OnPointerUp(PointerEventData eventData)
{
moveLeft = false;
Debug.Log(this.gameObject.name + " was released.");
}
void Update()
{
if (moveLeft)
// Move left code
}
}
This is a fairly dumb example, but should be at least something you can expand on.

Unity Unet Updating transform while clicked

I am currently trying to make a little drawing game where two players can draw at the same time over the network.
I am using a GameObject with a TrailRenderer to draw the lines.
Right now only the drawings of the host player are shown on both machines.
If a client player clicks and tries to draw I can see that a new object is spawned but the transform doesn't get updated. The spawned prefab has a NetworkIdentity (with Local Player Authority checked) and NetworkTransform attached to it. The following script is spawned by both players and also has a NetworkIdentity (with Local Player Authority checked).
I think I am actually doing something wrong with the CmdTrailUpdate and how to handle it but I can't really figure out what.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
public class TrailDrawer : NetworkBehaviour {
private Plane objPlane;
private GameObject currentTrail;
private Vector3 startPos;
public GameObject trail;
public void Start()
{
objPlane = new Plane(Camera.main.transform.forward * -1, this.transform.position);
}
// Update is called once per frame
void FixedUpdate() {
if (isLocalPlayer) {
if (Input.GetMouseButtonDown(0)) {
CmdSpawn();
} else if (Input.GetMouseButton(0)) {
CmdTrailUpdate();
}
}
}
[Command]
private void CmdTrailUpdate() {
Ray mRay = Camera.main.ScreenPointToRay(Input.mousePosition);
float rayDistance;
if (objPlane.Raycast(mRay, out rayDistance)) {
currentTrail.transform.position = mRay.GetPoint(rayDistance);
}
}
[Command]
private void CmdSpawn(){
Ray mRay = Camera.main.ScreenPointToRay(Input.mousePosition);
float rayDistance;
if (objPlane.Raycast(mRay, out rayDistance)) {
startPos = mRay.GetPoint(rayDistance);
currentTrail = (GameObject)Instantiate(trail, startPos, Quaternion.identity);
NetworkServer.Spawn(currentTrail);
}
}
}
I think your problem is:
[Command] means: Invoke the method from a client but only execute it on the server.
=> You are executing both methods CmdSpawn and CmdTrailUpdate only on the server.
But:
how shoud the server know your client's Input.mousePosition?
You don't want to raycast from the server's camera.main, but rather from the client's.
Solution:
Do both things local on the client and pass the position as parameter on the [Cmd] methods to the server.
Since you say the object already has a NetworkTransform, you wouldn't need to transmit the updated position to the server because NetworkTransform already does it for you. So calling CmdTrailUpdate from the client would not be neccesarry.
But: After spawning the object you have to tell the client who is calling CmdSpawn, which is his local currentTrail which's position he has to update. I would do this by simply passing the calling clients gameObject also to the CmdSpawn method and on the server call a [ClientRpc] method to set this client's currentTrail object.
(I'm assuming here, that the script you posted is attached diectly to the Player objects. If that is not the case, instead of the lines with this.gameObject you have to get the player's gameObject in a nother way.)
void FixedUpdate() {
if (!isLocalPlayer) return;
if (Input.GetMouseButtonDown(0)) {
// Do the raycast and calculation on the client
Ray mRay = Camera.main.ScreenPointToRay(Input.mousePosition);
float rayDistance;
if (objPlane.Raycast(mRay, out rayDistance)) {
startPos = mRay.GetPoint(rayDistance);
// pass the calling Players gameObject and the
// position as parameter to the server
CmdSpawn(this.gameObject, startPos);
}
} else if (Input.GetMouseButton(0)) {
// Do the raycast and calculation on the client
Ray mRay = Camera.main.ScreenPointToRay(Input.mousePosition);
float rayDistance;
if (objPlane.Raycast(mRay, out rayDistance)) {
// only update your local object on the client
// since they have NetworkTransform attached
// it will be updated on the server (and other clients) anyway
currentTrail.transform.position = mRay.GetPoint(rayDistance);
}
}
}
[Command]
private void CmdSpawn(GameObject callingClient, Vector3 spawnPosition){
// Note that this only sets currentTrail on the server
currentTrail = (GameObject)Instantiate(trail, spawnPosition, Quaternion.identity);
NetworkServer.Spawn(currentTrail);
// set currentTrail in the calling Client
RpcSetCurrentTrail(callingClient, currentTrail);
}
// ClientRpc is somehow the opposite of Command
// It is invoked from the server but only executed on ALL clients
// so we have to make sure that it is only executed on the client
// who originally called the CmdSpawn method
[ClientRpc]
private void RpcSetCurrentTrail(GameObject client, GameObject trail){
// do nothing if this client is not the one who called the spawn method
if(this.gameObject != client) return;
// also do nothing if the calling client himself is the server
// -> he is the host
if(isServer) return;
// set currentTrail on the client
currentTrail = trail;
}

Moving object from one postion to another in unity

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