Why the thrown objects are not thrown forward? - unity3d

I'm trying to throw objects the same direction the camera is pointing to (forward), but it is behaving really weird. Objects don't always get thrown forward, sometimes it gets thrown up or towards the player itself (backward). And I don't know why:
Here is a snippet of PickingUp class:
public float throwForce = 1000f;
...
private void PickUpObject(GameObject pickUpObj)
{
if (pickUpObj.GetComponent<Rigidbody>())
{
heldObj = pickUpObj;
heldObjRb = pickUpObj.GetComponent<Rigidbody>();
heldObjRb.isKinematic = true;
heldObjRb.transform.parent = holdPos.transform;
heldObj.layer = LayerNumber;
Physics.IgnoreCollision(heldObj.GetComponent<Collider>(), player.GetComponent<Collider>(), true);
reticle.color = new Color(1, 1, 1, 0.75f);
reticle.enabled = false;
}
}
void DropObject()
{
//re-enable collision with player
Physics.IgnoreCollision(heldObj.GetComponent<Collider>(), player.GetComponent<Collider>(), false);
heldObj.layer = 0; //object assigned back to default layer
heldObjRb.isKinematic = false;
heldObj.transform.parent = null; //unparent object
heldObj = null; //undefine game object
reticle.color = new Color(1, 1, 1, 0.75f);
reticle.enabled = true;
}
void ThrowObject()
{
//same as drop function, but add force to object before undefining it
Physics.IgnoreCollision(heldObj.GetComponent<Collider>(), player.GetComponent<Collider>(), false);
heldObj.layer = 0;
heldObjRb.isKinematic = false;
heldObj.transform.parent = null;
Vector3 camerDirection = camera.transform.forward;
heldObjRb.AddForce(camerDirection * throwForce);
// heldObjRb.constraints = RigidbodyConstraints.None;
heldObj = null;
reticle.color = new Color(1, 1, 1, 0.75f);
reticle.enabled = true;
}
All objects have Rigidbody and colliders attached to them. I get the same issue when dropping the objects too.

Try specifying the force mode as impulse on the AddForce method. The default is acceleration which can often require unreasonable large numbers to get the desired effect.
heldObjRb.AddForce(camerDirection * throwForce, ForceMode.Impulse);
A lot of people pointed out that your issue might be collision between the pickupobject and the players collider. You're using a method that tells the physics engine to ignore the collisions that I'm not familiar with, but should be fine to use for your purpose.
I would suggest enabling the collision after a short time delay, so the object has enough time to escape the players collider. You can do so with an invoke.
void ThrowObject()
{
//same as drop function, but add force to object before undefining it
heldObj.layer = 0;
heldObjRb.isKinematic = false;
heldObj.transform.parent = null;
Vector3 camerDirection = camera.transform.forward;
heldObjRb.AddForce(camerDirection * throwForce, ForceMode.Impulse);
// heldObjRb.constraints = RigidbodyConstraints.None;
reticle.color = new Color(1, 1, 1, 0.75f);
reticle.enabled = true;
Invoke("ResetCollision", 0.5f);//can be adjusted
}
public void ResetCollision(){
Physics.IgnoreCollision(heldObj.GetComponent<Collider>(), player.GetComponent<Collider>(), false);
heldObj = null;
}

Related

How to prevent player from flying when picking up objects underneath him?

For picking-up objects, I chose to make the picked-up object float in the middle of the screen in front of the camera following a certain point.
raycastPos = mainCamera.ScreenToWorldPoint(new Vector3(Screen.width / 2, Screen.height / 2, 0));
RaycastHit hit;
if (Physics.Raycast(raycastPos, mainCamera.transform.forward, out hit, maxDistance, 1 << interactableLayerIndex))
{
lookObject = hit.collider.transform.gameObject;
reticle.color = new Color(1, 0, 0, 0.75f);
}
else
{
lookObject = null;
reticle.color = new Color(1, 1, 1, 0.75f);
}
if (PickingUp)
{
if (currentlyPickedUpObject == null)
{
if (lookObject != null)
{
if (hit.distance >= minDistance)
{
PickUpObject();
}
}
}
}
public void PickUpObject()
{
physicsObject = lookObject.GetComponentInChildren<PhysicsObjects>();
currentlyPickedUpObject = lookObject;
// currPicked = true;
pickupRB = currentlyPickedUpObject.GetComponent<Rigidbody>();
pickupRB.constraints = RigidbodyConstraints.FreezeRotation;
physicsObject.playerInteractions = this;
}
I'm facing an issue with this, Whenever the player is standing over a pickable object and picks it up (while standing on it), the player flies on top of the object like this:
]
Here is another image from the Scene window :
You can see clearly here what's happening when the player picks up an object underneath him.
How can I prevent that from happening?
Put the picked up object in a new layer. Then disable any physics interaction between player and said layer. You will then observe that the picked up object and the player may intersect. To fix this, you can project the object outside of your player collider using a few more ray casts. Given that the object's collider is convex, calculations should be fairly simple.

How to stop raycast when there is a barrier blocking the object?

I'm using Raycast for picking up objects, whenever the player looking directly at a pickable object, the reticle colors turns to red meaning that it can be picked up. However, objects are still pickable even if there is a barrier between the raycast and the pickable object. like this image
as you can see the drawer is closed and the pickable object is inside, but the player still can pick it up!. it should pe pickable only when the object is exposed and the raycast hits it directly like this:
if (PickingUp)
{
if (heldObj == null) //if currently not holding anything
{
//perform raycast to check if player is looking at object within pickuprange
if (Physics.Raycast(transform.position, transform.TransformDirection(Vector3.forward), out hit, pickUpRange) && hit.transform.GetComponent<canPickUp_>() != null)
{
//make sure pickup tag is attached
// if (hit.transform.gameObject.tag == "canPickUp")
Debug.Log("Color should be changed");
if (hit.transform.CompareTag("TargetObj") && !targetObjectsList.Contains(hit.transform.gameObject))
{
/* if (aSource)
{
aSource.Play();
}*/
targetObjectsList.Add(hit.transform.gameObject);
}
PickUpObject(hit.transform.gameObject);
}
}
else
{
if (canDrop == true)
{
StopClipping(); //prevents object from clipping through walls
DropObject();
}
}
private void PickUpObject(GameObject pickUpObj)
{
if (pickUpObj.GetComponent<Rigidbody>()) //make sure the object has a RigidBody
{
heldObj = pickUpObj; //assign heldObj to the object that was hit by the raycast (no longer == null)
heldObjRb = pickUpObj.GetComponent<Rigidbody>(); //assign Rigidbody
heldObjRb.isKinematic = true;
heldObjRb.transform.parent = holdPos.transform; //parent object to holdposition
heldObj.layer = LayerNumber; //change the object layer to the holdLayer
//make sure object doesnt collide with player, it can cause weird bugs
Physics.IgnoreCollision(heldObj.GetComponent<Collider>(), player.GetComponent<Collider>(), true);
reticle.color = new Color(1, 1, 1, 0.75f);
reticle.enabled = false;
//transform.localScale = new Vector3(1 / heldObj.transform.localScale.x, 1 / heldObj.transform.localScale.y, 1 / heldObj.transform.localScale.z);
}
}
void DropObject()
{
//re-enable collision with player
Physics.IgnoreCollision(heldObj.GetComponent<Collider>(), player.GetComponent<Collider>(), false);
heldObj.layer = 0; //object assigned back to default layer
heldObjRb.isKinematic = false;
heldObj.transform.parent = null; //unparent object
heldObj = null; //undefine game object
reticle.color = new Color(1, 1, 1, 0.75f);
reticle.enabled = true;
}
How would I prevent this from happening?
As discussed in the comments the issue was that the "barrier" objects had their layers set to "Ignore Raycast", preventing the Physics.Raycast from reporting them as the hit.
Changing the layer to default (or any other layer that is not "Ignore Raycast") fixes the issue, as the "barrier" will be reported as the hit instead of the pickable object

Unity 2D Camera Zooms in when character moves, but properties of the camera do not change

After I click play the camera stays as it should be, but when I move my character the camera zooms in for no reason
The properties of the camera do not change at all, zoom and everything stays the same. Tryed changing from orthographic to perspective no change, move z axis no change, change scale no change, change resolution and no change, making the camera not a parent and no change it behaves the same as parent and as child
before character walks
after character walks
I dont think that there is something to do with the code but here is the code attached to my character, the camera behaves the same as child and as parent
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class movement : MonoBehaviour
{
public float speed = 5f;
public float jumpSpeed = 8f;
private float movementX = 0f;
private Rigidbody2D rigidBody;
public Transform groundCheckPoint;
public float groundCheckRadius;
public LayerMask groundLayer;
public bool isTouchingGround;
public SpriteRenderer box;
private bool canSpawn = true;
private bool canAnimateWalk = true;
private bool canAnimateIdle = true;
private bool canAnimateJump = true;
private bool stopJump = true;
private int spawningSpeed = 1000;
// Use this for initialization
void Start()
{
rigidBody = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
isTouchingGround = Physics2D.OverlapBox(groundCheckPoint.position,new Vector2(0.9f,0.1f),0f, groundLayer);
movementX = Input.GetAxis("Horizontal");
if (movementX > 0f)
{
if(canAnimateWalk==true && isTouchingGround)
{
canAnimateWalk = false;
StartCoroutine(AnimateWalk());
}
GetComponent<SpriteRenderer>().transform.localScale = new Vector3(2, 2, 1);
rigidBody.velocity = new Vector2(movementX * speed, rigidBody.velocity.y);
}
else if (movementX < 0f)
{
if (canAnimateWalk == true && isTouchingGround)
{
canAnimateWalk = false;
StartCoroutine(AnimateWalk());
}
GetComponent<SpriteRenderer>().transform.localScale = new Vector3(-2, 2, 1);
rigidBody.velocity = new Vector2(movementX * speed, rigidBody.velocity.y);
}
else
{
if(isTouchingGround)
{
StopCoroutine(AnimateWalk());
if(canAnimateIdle==true)
{
canAnimateIdle = false;
StartCoroutine(AnimateIdle());
}
}
rigidBody.velocity = new Vector2(0, rigidBody.velocity.y);
}
if (Input.GetButtonDown("Jump") && isTouchingGround)
{
canAnimateJump = false;
rigidBody.velocity = new Vector2(rigidBody.velocity.x, jumpSpeed);
StartCoroutine(AnimateJump());
}
else if(!isTouchingGround)
{
StopCoroutine(AnimateWalk());
}
}
IEnumerator AnimateJump()
{
Debug.Log("Animating Jump");
int counter = 0;
while (counter < 10)
{
counter++;
GetComponent<SpriteRenderer>().sprite = Resources.Load<Sprite>("img/j" + counter);
yield return new WaitForSeconds(0.1f);
if(isTouchingGround==true)
{
break;
}
}
while(!isTouchingGround)
{
GetComponent<SpriteRenderer>().sprite = Resources.Load<Sprite>("img/j10");
yield return new WaitForSeconds(0.1f);
}
GetComponent<SpriteRenderer>().sprite = Resources.Load<Sprite>("img/i1");
canAnimateWalk = true;
canAnimateJump = true;
}
IEnumerator AnimateIdle()
{
int counter = 0;
while(Input.GetAxis("Horizontal")==0 && counter <10 && rigidBody.velocity.y==0)
{
counter++;
GetComponent<SpriteRenderer>().sprite = Resources.Load<Sprite>("img/i"+counter);
yield return new WaitForSeconds(0.2f);
}
canAnimateIdle = true;
}
IEnumerator AnimateWalk()
{
int counter = 0;
while (Input.GetAxis("Horizontal")!=0 && counter < 8 && rigidBody.velocity.y==0)
{
counter++;
GetComponent<SpriteRenderer>().sprite = Resources.Load<Sprite>("img/g" + counter);
yield return new WaitForSeconds(0.08f);
}
canAnimateWalk = true;
}
}
What could it be? I tried everything I think
GetComponent<SpriteRenderer>().transform.localScale = new Vector3(-2, 2, 1);
If your movement script is attached to your "guy" gameobject, then you are changing the (local) scale of it. All children will also scale accordingly.
Since your camera is a child of guy, it will scale and produce the result you see.
Try unparenting the Camera from your guy and create a seperate script that follows your guy and attach that to your Camera.
I solved my problem
The issue was in the character scaling. The camera did not change but the size of the character did making me believe that there was a zoom in.
My character x and y scale is 1 and 1 but I used 2 and 2 scale on move
The scale was used to rotate my character when it moves left and right

Unity network transform problem - Client spawning objects at wrong location

I'm working on a Fortnite-esque game for Unity.
Player spawns in, has ability to spawn cubes to make a "base".
Everything works perfectly well in mono but I'm having a strange issue with networking. On the server, my player can spawn the cubes perfectly according to the Raycast hitpoint while on the client, even though the player is a clone of the Player prefab, the spawned objects always either end up at the world origin, rather than Raycast hitpoint -or- if I remove if (!isPlayLocal) {return;} from the player's script containing Raycast info, the cube spawns inaccurately and without its corresponding material.
I'll try and pinpoint the code so I can place it here but I imagine it could be a number of things.
Local Player Auth is checked off on spawn prefabs and all prefabs have been registered in Network Manager.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
public class BuildingSystemNew : NetworkBehaviour
{
[SerializeField] private Camera playerCamera;
[SerializeField] private GameObject blockTemplatePrefab;
[SerializeField] private GameObject blockPrefab;
[SerializeField] private Material templateMaterial;
[SerializeField] private LayerMask buildableSurfacesLayer;
private bool buildModeOn = false;
private bool canBuild = false;
private bool crossHairOn = false;
private BlockSystem bSys;
public Texture2D crosshairImage;
private int blockSelectCounter = 0;
private GameObject weapon;
private Vector3 buildPos;
private GameObject currentTemplateBlock;
private void Start()
{
bSys = GetComponent<BlockSystem>();
}
private void Update()
{
if (isLocalPlayer == false)
return;
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
if (Input.GetKeyDown("e"))
{
buildModeOn = !buildModeOn;
if (buildModeOn)
{
// weapon.SetActive(false);
crossHairOn = true;
}
else
{
// weapon.SetActive(true);
crossHairOn = false;
}
}
if (Input.GetKeyDown("r"))
{
blockSelectCounter++;
if (blockSelectCounter >= bSys.allBlocks.Count) blockSelectCounter = 0;
}
if (buildModeOn)
{
RaycastHit buildPosHit;
if (Physics.Raycast(playerCamera.ScreenPointToRay(new Vector3(Screen.width / 2, Screen.height / 2, 0)), out buildPosHit, 10, buildableSurfacesLayer))
{
Vector3 point = buildPosHit.point;
buildPos = new Vector3(Mathf.Round(point.x), Mathf.Round(point.y), Mathf.Round(point.z));
canBuild = true;
}
else
{
Destroy(currentTemplateBlock.gameObject);
canBuild = false;
}
}
if (!buildModeOn && currentTemplateBlock != null)
{
Destroy(currentTemplateBlock.gameObject);
canBuild = false;
}
if (canBuild && currentTemplateBlock == null)
{
currentTemplateBlock = Instantiate(blockTemplatePrefab, buildPos, Quaternion.identity);
currentTemplateBlock.GetComponent<MeshRenderer>().material = templateMaterial;
}
if (canBuild && currentTemplateBlock != null)
{
currentTemplateBlock.transform.position = buildPos;
if (Input.GetMouseButtonDown(0))
{
CmdPlaceBlock();
}
else if (Input.GetMouseButtonDown(1))
{
CmdDestroyBlock();
}
}
}
[Command]
public void CmdPlaceBlock()
{
GameObject newBlock = Instantiate(blockPrefab, buildPos, Quaternion.identity);
Block tempBlock = bSys.allBlocks[blockSelectCounter];
newBlock.name = tempBlock.blockName + "-Block";
newBlock.GetComponent<MeshRenderer>().material = tempBlock.blockMaterial;
NetworkServer.SpawnWithClientAuthority(newBlock, connectionToClient);
}
[Command]
private void CmdDestroyBlock()
{
RaycastHit hit;
Ray ray = playerCamera.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit))
{
var objectHit = hit.collider.gameObject;
if (hit.collider.gameObject.tag == "Block")
{
Destroy(objectHit);
}
}
}
void OnGUI()
{
if (crossHairOn == true)
{
float xMin = (Screen.width / 2) - (crosshairImage.width / 2);
float yMin = (Screen.height / 2) - (crosshairImage.height / 2);
GUI.DrawTexture(new Rect(xMin, yMin, crosshairImage.width, crosshairImage.height), crosshairImage);
}
}
}
The Problem
You are calling
[Command]
public void CmdPlaceBlock()
{
GameObject newBlock = Instantiate(blockPrefab, buildPos, Quaternion.identity);
Block tempBlock = bSys.allBlocks[blockSelectCounter];
newBlock.name = tempBlock.blockName + "-Block";
newBlock.GetComponent<MeshRenderer>().material = tempBlock.blockMaterial;
NetworkServer.SpawnWithClientAuthority(newBlock, connectionToClient);
}
without a parameter.
A Command is called on the client but executed on the Server
=> with the local variables of the Server!
So e.g. buildPos will always have the default value 0,0,0 on the server since due to
if(!isLocalPlayer) return;
later the line
buildPos = new Vector3(Mathf.Round(point.x), Mathf.Round(point.y), Mathf.Round(point.z));
is never executed on the server. The same also applies e.g. to blockSelectCounter and probably other values your CmdPlaceBlock depends on.
Solution
You should pass your client's buildPos value (and also all other values that are different on client and server) to the server command so the server knows at which correct position the new object should be placed:
//...
if (Input.GetMouseButtonDown(0))
{
CmdPlaceBlock(buildPos);
}
//...
[Command]
public void CmdPlaceBlock(Vector3 spawnPosition)
{
GameObject newBlock = Instantiate(blockPrefab, spawnPosition, Quaternion.identity);
Block tempBlock = bSys.allBlocks[blockSelectCounter];
newBlock.name = tempBlock.blockName + "-Block";
newBlock.GetComponent<MeshRenderer>().material = tempBlock.blockMaterial;
NetworkServer.SpawnWithClientAuthority(newBlock, connectionToClient);
}
I added only an example for the position knowing it will always be different on client and server. But it also might apply to your other values like e.g. blockSelectCounter.
Do this change for all values that have to be the ones of the client and not the ones of the server.
Note that the types that can be passed between networking methods are limited! You can't pass e.g. any component references.
The allowed argument types are;
Basic type (byte, int, float, string, UInt64, etc)
Built-in Unity math type (Vector3, Quaternion, etc),
Arrays of basic types
Structs containing allowable types
NetworkIdentity
NetworkInstanceId
NetworkHash128
GameObject with a NetworkIdentity component attached.
Additional Hints
For readability and amount of lines you should change things like e.g.
if (buildModeOn)
{
// weapon.SetActive(false);
crossHairOn = true;
}
else
{
// weapon.SetActive(true);
crossHairOn = false;
}
to simply
// weapon.SetActive(!buildModeOn);
crossHairOn = buildModeOn;
and check bools not like
if (isLocalPlayer == false)
but rather
if(!isLocalPlayer)
It simply reads/writes easier ;)

Unity- Moving an Object to the left on a Scrolling Background

I am trying to create a simple Infinite Runner game on Unity and ran into a problem. The task is to make a ball spawn on the floor and immediately begin to roll to the left towards the Player. I have tried a number of ways to implement it, but it does not seem to work. Here is my most recent attempt:
public class ObstaclePool : MonoBehaviour {
public GameObject columnPrefab;
public GameObject ballPrefab;
public int obstaclePoolSize = 5;
public float spawnRate = 3f;
private GameObject[] obstacles;
private int currentObstacle = 0;
private Vector2 objectPoolPosition = new Vector2(-15, -25);
private float timeSinceLastSpawned;
private float spawnXPosition;
private bool hasCalled = false;
private int dice;
bool beforeBall = false;
// Use this for initialization
void Start () {
timeSinceLastSpawned = 0f;
SetupObstacles();
}
private void SetupObstacles()
{
obstacles = new GameObject[obstaclePoolSize];
for (int i = 0; i < obstaclePoolSize; i++)
{
dice = Random.Range(1, 3);
if (dice == 1)
{
obstacles[i] = (GameObject)Instantiate(columnPrefab, objectPoolPosition, Quaternion.identity);
}
else if (dice == 2)
{
obstacles[i] = (GameObject)Instantiate(ballPrefab, objectPoolPosition, Quaternion.identity);
}
}
}
// Update is called once per frame
void Update () {
timeSinceLastSpawned += Time.deltaTime;
if (GameControl.instance.gameOver == false && timeSinceLastSpawned >= spawnRate)
{
timeSinceLastSpawned = 0f;
if (hasCalled == false)
{
spawnXPosition = 10f;
hasCalled = true;
}
else
{
spawnXPosition = Random.Range(6f, 10f);
}
if (obstacles[currentObstacle].transform.tag == "Ball")
{
spawnXPosition = Random.Range(9f, 10f);
obstacles[currentObstacle].transform.position = new Vector2(spawnXPosition, -1.84f);
ballPrefab.GetComponent<Rigidbody2D>().AddForce(new Vector2(-100f, 0) * 5);
beforeBall = true;
}
else {
if (beforeBall == true)
{
spawnXPosition = Random.Range(9f, 10f);
beforeBall = false;
}
obstacles[currentObstacle].transform.position = new Vector2(spawnXPosition, -7.08f);
Debug.Log(spawnXPosition);
}
currentObstacle++;
if (currentObstacle >= obstaclePoolSize)
{
currentObstacle = 0;
SetupObstacles();
hasCalled = false;
}
}
}
}
For a quick explanation of my code: I have an array of size 5. It holds the obstacles that I have created. When deciding what to put inside the array, I generated a random number (1 or 2). If it's a 1, I put in a column. If it's a 2, I put in a ball. These obstacles are spawned off-screen. Then, I move them in the actual scene after using random number to determine the X position.
This part in particular is where I try to implement it:
if (obstacles[currentObstacle].transform.tag == "Ball")
{
spawnXPosition = Random.Range(9f, 10f);
obstacles[currentObstacle].transform.position = new Vector2(spawnXPosition, -1.84f);
ballPrefab.GetComponent<Rigidbody2D>().AddForce(new Vector2(-100f, 0) * 5);
beforeBall = true;
}
I may have some remnants of stuff that I have been testing out, so some of the code may seem redundant and messy.
I also tried using Translate and Velocity with no success. I also have a ScrollingObject code and a RepeatingBackground code. I placed the ScrollingObject code in the Ball prefab too. (Also, tried taking it out -> ball rolls to the right). These codes come from the Unity tutorial.
RepeatingBackground:
public class RepeatingBackground : MonoBehaviour {
private BoxCollider2D groundCollider;
private float groundHorizontalLength;
// Use this for initialization
private void Awake () {
groundCollider = GetComponent<BoxCollider2D>();
groundHorizontalLength = groundCollider.size.x;
}
// Update is called once per frame
private void Update () {
if (transform.position.x < -groundHorizontalLength)
{
RepositionBackground();
}
}
private void RepositionBackground()
{
Vector2 groundOffSet = new Vector2(groundHorizontalLength * 2f, 0);
transform.position = (Vector2)transform.position + groundOffSet;
}
}
ScrollingObjects:
public class ScrollingObject : MonoBehaviour {
private Rigidbody2D rb2d;
// Use this for initialization
void Start () {
rb2d = GetComponent<Rigidbody2D>();
rb2d.velocity = new Vector2(GameControl.instance.scrollSpeed, 0);
}
// Update is called once per frame
void Update () {
if (GameControl.instance.gameOver == true)
{
rb2d.velocity = Vector2.zero;
}
}
}
It looks like you simply named the wrong object in your first example.
if (obstacles[currentObstacle].transform.tag == "Ball")
{
spawnXPosition = Random.Range(9f, 10f);
obstacles[currentObstacle].transform.position = new Vector2(spawnXPosition, -1.84f);
ballPrefab.GetComponent<Rigidbody2D>().AddForce(new Vector2(-100f, 0) * 5);
beforeBall = true;
}
Notice how here you added the force to the prefab, not an instantiated version of the prefab.
I played around a bit more and got it to work. I just wrote another script that added force to the object. I still do not understand why my original way did not work though.