I am trying to make a platformer game. In the platformer game has a moving platform that makes everything it touches its transform's child while it touches it. Interestingly, whenever a stone(A movable object with a rigidbody and a polygon collider) touches the moving platform, the stone's scale goes haywire. Even though the scale reads the same on the transform component, it appears to be larger or smaller than it really is when touching it. When it stops touching the platform, the stone appears normal. Can anyone help me. Thank you.
This is the moving platform script that moves the platform around.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MoveTwoTransforms : MonoBehaviour
{
public Transform pointA;
public Transform pointB;
public bool HasReachedA;
public bool HasReacedB;
// Start is called before the first frame update
void Start()
{
transform.position = pointA.position;
HasReacedB = true;
HasReachedA = false;
StartCoroutine(GlideAround());
}
// Update is called once per frame
void Update()
{
}
public IEnumerator GlideAround()
{
while (true)
{
while (HasReachedA == false)
{
yield return new WaitForEndOfFrame();
transform.position = Vector2.Lerp(transform.position, pointA.position, 0.01f);
if ((Mathf.Abs(Vector2.Distance(pointA.position, transform.position)) < 0.01f))
{
HasReacedB = false;
HasReachedA = true;
}
}
while (HasReacedB == false)
{
yield return new WaitForEndOfFrame();
transform.position = Vector2.Lerp(transform.position, pointB.position, 0.01f);
if ((Mathf.Abs(Vector2.Distance(pointB.position, transform.position)) < 0.01f))
{
HasReacedB = true;
HasReachedA = false;
}
}
}
}
private void OnCollisionEnter2D(Collision2D collision)
{
if((collision.gameObject.tag == "Stone"|| collision.gameObject.tag == "Player") && (collision.transform.position.y - collision.transform.lossyScale.y / 2 >= transform.position.y))
{
collision.transform.parent = transform;
}
}
private void OnCollisionExit2D(Collision2D collision)
{
collision.transform.parent = null;
}
}
This is the stone script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RememberPositions : MonoBehaviour
{
public Vector3 StartingPosition;
public Vector3 StartingRotation;
public Vector3 StartingScale;
float StartRotation;
// Start is called before the first frame update
void Start()
{
StartingPosition = new Vector3(transform.position.x, transform.position.y, 0);
StartingRotation = new Vector3(0, 0, transform.position.z);
StartingScale = new Vector3(transform.localScale.x, transform.localScale.y, transform.localScale.z);
}
// Update is called once per frame
void Update()
{
transform.localScale = StartingScale;
}
}
There are no errors whatsoever and the rigidbody seems to be changing to the shape of the stone. Can anyone please specify the correct code I should use? Thank you.
If your moving platform is scaled, your stone will get scaled too. There are workarounds shown here to avoid this issue. One of them is adding a child GameObject to the platform, and moving the collision handling from the platform to that child.
Related
I am moving a sprite that has the following attached BoxCollider2D, Rigidbody2D (Dynamic set with simulated off) and a SpriteRenderer. Problem the trigger is not being set with the following code. I come from a 3D background and this would have triggered but its not in 2D not sure why, maybe things are different?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MonsterController : MonoBehaviour
{
private MonsterInformation info;
private bool isMoving;
private Vector3 wantedPosition;
private Vector3 MaxPosition;
private Vector3 MinPosition;
private bool isAttacking;
// Start is called before the first frame update
void Start()
{
info = MonsterDatabase.Instance.MonsterLookup("Slime");
MaxPosition = new Vector3(transform.position.x + info.WanderingDistance,
transform.position.y + info.WanderingDistance, transform.position.z);
MinPosition = new Vector3(transform.position.x - info.WanderingDistance,
transform.position.y - info.WanderingDistance, transform.position.z);
}
public void Update()
{
if(!isMoving)
{
wantedPosition.x = Random.Range(MinPosition.x, MaxPosition.x);
wantedPosition.y = Random.Range(MinPosition.y, MaxPosition.y);
wantedPosition.z = transform.position.z;
isMoving = true;
}
if(isAttacking)
{
return;
}
transform.position = Vector2.MoveTowards(transform.position, wantedPosition,
Time.deltaTime * info.WalkingSpeed);
if(transform.position == wantedPosition)
{
isMoving = false;
}
}
public void OnTriggerEnter2D(Collider2D other)
{
Debug.Log($"Triggered {other.name}");
}
}
I am guessing that this needs to be moved through physics? transform.position = Vector2.MoveTowards(transform.position, wantedPosition,
Time.deltaTime * info.WalkingSpeed);
Also thank you for your time to respond to this question even if it seems like its stupid and I didn't do enough research, the main problem is Unity3D in a 3D environment I would collide with a box collider without having to move through physics so I am stumped as to why its not working in 2D.
I'm making a breakout game in 3D and it's my first time making a game and using Unity so I'm a bit clueless. I've got to the point where my game works fine up until the ball goes off the screen and into the "dead zone".
Can someone advise how to respawn the paddle and ball together and carry on with the game?
I've included my ball and paddle scripts below, I have a script for the bricks as well but not sure that was relevant. I also made a prefab of the ball and paddle together but no idea what to do with it.
Thanks to anyone who can help :)
Code for my ball
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BallScript : MonoBehaviour
{
public Rigidbody rbody;
public float MinVertMovement = 0.1f;
public float MinSpeed = 10f;
public float MaxSpeed = 10f;
private bool hasBeenLaunched = false;
void Start()
{
}
private float minVelocity = 10f;
private Vector3 lastFrameVelocity;
void FixedUpdate()
{
if (hasBeenLaunched == false)
{
if (Input.GetKey(KeyCode.Space))
{
Launch();
}
}
if (hasBeenLaunched)
{
Vector3 direction = rbody.velocity;
direction = direction.normalized;
float speed = direction.magnitude;
if (direction.y>-MinVertMovement && direction.y <MinVertMovement)
{
direction.y = direction.y < 0 ? -MinVertMovement : MinVertMovement;
direction.x = direction.x < 0 ? -1 + MinVertMovement : 1 - MinVertMovement;
rbody.velocity = direction * MinSpeed;
}
if (speed<MinSpeed || speed>MaxSpeed)
{
speed = Mathf.Clamp(speed, MinSpeed, MaxSpeed);
rbody.velocity = direction*speed;
}
}
lastFrameVelocity = rbody.velocity;
}
void OnCollisionEnter(Collision collision)
{
Bounce(collision.contacts[0].normal);
}
private void Bounce(Vector3 collisionNormal)
{
var speed = lastFrameVelocity.magnitude;
var direction = Vector3.Reflect(lastFrameVelocity.normalized, collisionNormal);
Debug.Log("Out Direction: " + direction);
rbody.velocity = direction * Mathf.Max(speed, minVelocity);
}
public void Launch()
{
rbody = GetComponent<Rigidbody>();
Vector3 randomDirection = new Vector3(-5f, 10f, 0);
randomDirection = randomDirection.normalized * MinSpeed;
rbody.velocity = randomDirection;
transform.parent = null;
hasBeenLaunched = true;
}
}
Code for my paddle
public class PaddleScript : MonoBehaviour
{
private float moveSpeed = 15f;
void Start()
{
}
void Update()
{
if (Input.GetKey(KeyCode.RightArrow) && transform.position.x<9.5)
transform.Translate(moveSpeed *Input.GetAxis("Horizontal")*Time.deltaTime, 0f, 0f);
if (Input.GetKey(KeyCode.LeftArrow) && transform.position.x>-7.5)
transform.Translate(moveSpeed *Input.GetAxis("Horizontal")*Time.deltaTime, 0f, 0f);
}
}
The simplest thing you can do to check wether the ball goes off screen is to place a trigger immediately off the perimeter of the camera, and add an OnTriggerEnter2D method to your ball.
/* Inside the ball script */
private void OnTriggerEnter() { // Use the 2D version if you're using 2D colliders
/* Respawning stuff */
}
Since you may want a bunch of different things to happen when that method triggers, you may want to use a Unity Event, which is not the king of performance but it probabily doesn't matter for a game like breakout.
using UnityEngine.Events;
public class BallScript : MonoBehaviour
{
public UnityEvent onBallOut;
/* ... */
private void OnTriggerEnter() {
onBallOut.Invoke();
}
}
You then probabily want a Respawn() method (not Reset() because that's a default MonoBehaviour call) which places the ball back to its original position, which you can store in a field as soon as the scene loads:
private Vector3 defaultPosition;
private void Start() {
defaultPosition = transform.position;
}
PS: If you aren't using the Start() method in your paddle script then remove it, cause it will be called by Unity even if empty.
I can't solve this problem where i want to hide and unhide a gameobject(UI) when i move using a VR controller. It came out fine in the editor but after build for the oculus quest(android), it stays inactive. anyone can help me? i put a list of UI that need to be hidden and wanted to set it active when the camera is not moving and hide when it is moving
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR;
using UnityEngine.XR.Interaction.Toolkit;
using UnityEngine.UI;
public class ContinuousMovement : MonoBehaviour
{
//This script is used to make the VR Camera moves continuous using the analog
public float speed = 1;
public XRNode inputSource;
public float gravity = -9.81f;
public LayerMask groundLayer;
public float additionalHeight = 0.2f;
public List<GameObject> UiToBeHidden;
private float fallingSpeed;
private XRRig rig;
private Vector2 inputAxis;
private CharacterController character;
// Start is called before the first frame update
void Start()
{
character = GetComponent<CharacterController>();
rig = GetComponent<XRRig>();
foreach (GameObject ui in UiToBeHidden)
{
ui.SetActive(true);
}
}
// Update is called once per frame
void Update()
{
CapsuleFollowHeadset();
InputDevice device = InputDevices.GetDeviceAtXRNode(inputSource);
device.TryGetFeatureValue(CommonUsages.primary2DAxis, out inputAxis);
Quaternion headYaw = Quaternion.Euler(0, rig.cameraGameObject.transform.eulerAngles.y, 0);
Vector3 direction = headYaw * new Vector3(inputAxis.x, 0, inputAxis.y);
character.Move(direction * Time.fixedDeltaTime * speed);
if (inputAxis.x == 0 && inputAxis.y == 0)
{
Debug.Log("static");
foreach (GameObject ui in UiToBeHidden)
{
ui.SetActive(true);
}
}
else
{
Debug.Log("moving");
foreach (GameObject ui in UiToBeHidden)
{
ui.SetActive(false);
}
}
}
private void FixedUpdate()
{
//gravity
bool isGrounded = CheckIfGrounded();
if (isGrounded)
fallingSpeed = 0;
else
fallingSpeed += gravity * Time.fixedDeltaTime;
character.Move(Vector3.up * fallingSpeed * Time.fixedDeltaTime);
}
void CapsuleFollowHeadset()
{
character.height = rig.cameraInRigSpaceHeight + additionalHeight;
Vector3 capsuleCenter = transform.InverseTransformPoint(rig.cameraGameObject.transform.position);
character.center = new Vector3(capsuleCenter.x, character.height /2 + character.skinWidth, capsuleCenter.z);
}
bool CheckIfGrounded()
{
//tells us if on ground
Vector3 rayStart = transform.TransformPoint(character.center);
float rayLength = character.center.y + 0.01f;
bool hasHit = Physics.SphereCast(rayStart, character.radius, Vector3.down, out RaycastHit hitInfo, rayLength, groundLayer);
return hasHit;
}
}
I had exactly the same problem. The solution that works for me is changing the build setting.
Build Setting --> Player Settings --> Player --> Other Setting --> Api Compatibility level ( Change from .Net Standard 2.0 --> .Net 4.x)
That should work for you as well!
So, I am trying to create a soccer game from scratch... all I have done until now, is setting up the ball. This is how I want it to work: When the player collides with the ball, the ball jumps forward a bit. If you start running the ball will be pushed further away.
Now, here is my script for the ball (I am using the standard FPSController as character):
using UnityEngine;
using System.Collections;
public class BallController : MonoBehaviour {
private Rigidbody rb;
public GameObject character;
public float moveSpeed = 1000;
public float shootSpeed = 2000;
bool isTurnedUp = false;
bool isTurnedDown = false;
bool done = false;
// Use this for initialization
void Start () {
rb = GetComponent<Rigidbody>();
}
// Update is called once per frame
void FixedUpdate () {
//Debug.Log(isTurnedUp + ", " + isTurnedDown);
switch (character.GetComponent<UnityStandardAssets.Characters.FirstPerson.FirstPersonController>().m_IsWalking)
{
case true:
if (isTurnedUp == false)
{
moveSpeed = moveSpeed / 1.4f;
isTurnedUp = true;
isTurnedDown = false;
}
break;
case false:
if (isTurnedDown == false)
{
moveSpeed = moveSpeed * 1.4f;
isTurnedDown = true;
isTurnedUp = false;
}
break;
}
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
if (Vector3.Distance(gameObject.transform.position, character.transform.position) <= 5)
{
float distance = Vector3.Distance(gameObject.transform.position, character.transform.position);
}
}
}
void OnCollisionEnter(Collision collision) {
FixedUpdate();
if (done == false) {
rb.AddForce(Vector3.forward * moveSpeed, ForceMode.Impulse);
done = true;
}
else {
done = false;
}
}
//other
void OnDrawGizmosSelected()
{
Gizmos.color = Color.yellow;
Gizmos.DrawWireSphere(transform.position, 2);
}
}
My problem is that the ball doesn't behave how I want it... it feels like it's about luck if the ball will jump forward when I touch it. Can someone tell me what I did wrong?
Inside of OnCollisionEnter you need to ensure the ball can only be kicked by the player. You can check whether or not the player has collided with the ball by checking the name or tag of the collision. The following example uses the name and assumes your player GameObject is named "Player".
Remove the done flag since this will only allow the player to kick the ball every other time they collide, and remove the FixedUpdate() call since FixedUpdate() is already called automatically every physics calculation.
Finally, if you want to kick the ball away from the player, then you need to calculate the direction away from the collision point instead of using Vector3.forward as seen below.
void OnCollisionEnter(Collision collision)
{
if(collision.gameObject.name == "Player")
{
Vector3 direction = (collision.transform.position - transform.position).normalized;
rb.AddForce(-direction * moveSpeed, ForceMode.Impulse);
}
}
I am making a 2d game. My problem is that while playing, if the player holds jump and is under a BoxCollider2D, the player will not fall until they release jump.
My player GameObject consists of a sprite renderer, a dynamic rigidbody2d with gravity on and a boxcollider2d.
Here are my movement scripts:
1:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class JumpScript : MonoBehaviour {
[Range(1, 10)]
public float jumpVelocity;
[Range(0,10)]
public float speed;
private bool jumpQueue = false;
private bool boolin=false;
void Update()
{
//Friggin fall, loser
//Jumping
///*
if (Input.GetButton("Jump")&& GetComponent<Rigidbody2D> ().velocity.y==0)
{
GetComponent<Rigidbody2D>().velocity = Vector2.up * jumpVelocity;
}
//*/
//jumpQueue?
/*
if(Input.GetButtonDown("Jump"))
{
jumpQueue = true;
}*/
//Right Movement
if (Input.GetKey(KeyCode.D))
{
GetComponent<Rigidbody2D>().velocity = new Vector2(1*speed, GetComponent<Rigidbody2D>().velocity.y);
boolin = true;
}
if(Input.GetKeyUp(KeyCode.D))
{
GetComponent<Rigidbody2D>().velocity = new Vector2(0, GetComponent<Rigidbody2D>().velocity.y);
boolin = false;
}
//Left Movement
if (Input.GetKey(KeyCode.A))
{
GetComponent<Rigidbody2D>().velocity = new Vector2(-1*speed, GetComponent<Rigidbody2D>().velocity.y);
boolin = true;
}
if (Input.GetKeyUp(KeyCode.A))
{
GetComponent<Rigidbody2D>().velocity = new Vector2(0, GetComponent<Rigidbody2D>().velocity.y);
boolin = false;
}
//No movement?
if (Input.GetKey(KeyCode.D) && Input.GetKey(KeyCode.A))
{
GetComponent<Rigidbody2D>().velocity = new Vector2(0, GetComponent<Rigidbody2D>().velocity.y);
boolin = false;
}
//Time to handle animation, boios.
Rigidbody2D rb = GetComponent<Rigidbody2D>();
bool schwomp = false;
bool schwift = false;
if(rb.velocity.y>0)
{
schwomp = true;
}
if(rb.velocity.y<0)
{
schwift = true;
}
Animator anim = GetComponent<Animator>();
if (boolin)
{
anim.SetInteger("Boolin", 1);
/*if (!anim.GetBool("expand"))
{
anim.SetBool("expand", true);
anim.Play("running");
}*/
}
else
{
anim.SetInteger("Boolin", 0);
/*
if(anim.GetBool("expand"))
{
anim.SetBool("expand", false);
anim.Play("Idle");
}*/
}
if(schwomp)
{
//anim.SetInteger("Boolin", 2);
}
if(schwift)
{
//anim.SetInteger("Boolin", 3);
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BetterJumper : MonoBehaviour {
public float fallMultiplier = 2.5f;
public float lowJumpMultiplier = 2f;
Rigidbody2D rb;
void Awake()
{
rb = GetComponent<Rigidbody2D>();
}
void Update()
{
if(rb.velocity.y<0)
{
rb.velocity += Vector2.up*Physics2D.gravity.y*(fallMultiplier-1)*Time.deltaTime;
}
else if(rb.velocity.y>0&&!Input.GetButton("Jump"))
{
rb.velocity += Vector2.up * Physics2D.gravity.y * (lowJumpMultiplier - 1) * Time.deltaTime;
}
}
}
Thank you so much in advance!
You're using Input.GetKey() which will poll the key every frame. This means more and more velocity is added the longer you hold jump. You've effectively built a jetpack rather than a jump force.
You should use Input.GetKeyDown() which will only fire once when a key is pressed down, then has to be released and pressed again in order to re-trigger. You then need to apply a single sufficiently strong vertical force using RigidBody.AddForce() to make the character jump, rather than adding continuously to the velocity.
Additionally, you should really be caching the result of your GetComponent<Rigidbody2D>() call when the script either wakes up or starts so that you're not calling it continuously; Each one of those calls takes processing time. Also, you should be using FixedUpdate() for physics.
public class ExampleClass : MonoBehaviour {
public float thrust;
public Rigidbody rb; // make the rigidbody variable available anywhere in the class
void Start() {
// cache the rigidbody component to the variable once on start
rb = GetComponent<Rigidbody>();
}
void FixedUpdate() {
// use the variable reference from now on rather than making GetComponent calls
rb.AddForce(transform.up * thrust);
}
}
Soviut's answer explains it quite nicely, but there's more that you need to know. You're directly manipulating the velocity, which overrides the effect of any forces, including gravity(despite the fact you're applying it manually). See the documentation.
As demonstrated in Soviut's answer you should be applying forces and impulses, letting the physics engine determine the velocity. The only time you should set velocity directly is when you're building a simulation that intentionally has unrealistic physics(i.e. retro platformers). Even in that case, bear in mind that doing so creates a lot more work, because you'll need to factor in every little thing that creates movement. This means you're essentially re-inventing the physics engine.