There are 2 GameObjects; a platform and a ball.
The ball is controlled via a custom controller, and the platform moves via an animation.
Components
platform
+ rigidbody
+ box collider
ball
+ rigidbody
+ sphere collider
When the ball comes in contact with the platform, the ball should stop its current velocity and attain the velocity of the platform it is in contact with. However currently, the ball just falls straight through the platform as if there is no colliders attached.
Code of Player:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class PlayerController : MonoBehaviour {
public Text winText;
public float speed;
public Text countText;
public GameObject light;
public GameObject player;
private Rigidbody rb;
private int count;
private int a = 0;
private int b = 0;
private int c = 0;
void Start ()
{
rb = GetComponent<Rigidbody>();
count = 0;
SetCountText ();
winText.text = "";
}
void FixedUpdate()
{
if (player.transform.position.y < -15) {
transform.position = new Vector3(a, b, c);
}
float moveHorizontal = Input.GetAxis ("Horizontal");
float moveVertical = Input.GetAxis ("Vertical");
Vector3 movement = new Vector3 (moveHorizontal, 0.0f, moveVertical);
rb.AddForce (movement * speed );
}
void OnTriggerEnter (Collider other)
{
if (other.gameObject.CompareTag ("Pick Up"))
{
other.gameObject.SetActive (false);
count = count + 1;
SetCountText ();
}
if (other.gameObject.CompareTag ("Check point"))
{
other.gameObject.SetActive (false);
light.gameObject.SetActive (false);
a = 0;
b = -10;
c = 96;
}
}
void SetCountText ()
{
countText.text = "Score: " + count.ToString();
if (count >= 8)
{
winText.text = "You Win!";
}
}
}
You said you are using a custom controller. Please make sure that you are not using Transform() to change the ball's position manually and move your ball as this defies the physics laws in unity. Instead use Rigidbody.MovePosition().
More at Unity docs
Make sure you have all those game objects in same Z axis.
Throw a debug.log() message in your OnCollisionEnter2D() method to see if they are actually colliding.
Could you also check for the type of Colliders you are using.
More Details about Collision in unity :
https://docs.unity3d.com/Manual/CollidersOverview.html
Also if it a custom controller make sure that something is not changing the position of ball to go below the platform.
Fast moving objects need the dynamic ContinuousDynamic or Continuous mode to work reliable
Make sure both rigidbodies are kinetic and the colliders are not triggers. Thats what really worked for me.
Related
I wanted to make an endless runner game where the player can jump and he moves forward automatically, so, for the forward movement i wanted to use the rigidbody velocity function in order to make the movement smoother and for the jump i made a custom function. My problem is, when i use in my update method the velocity function alone, it works, when i use it with my jump function it stops working. What can i do?. I tried changing the material of the floor and of the player's box collider to a really slippery one but nothing
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MovePrima : MonoBehaviour
{
[SerializeField]
private float _moveSpeed = 0f;
[SerializeField]
private float _gravity = 9.81f;
[SerializeField]
private float _jumpspeed = 3.5f;
public float speedAccel = 0.01f;
private CharacterController _controller;
private float _directionY;
public float startSpeed;
public float Yspeed = 10f;
private int i = 0;
public float touchSensibility = 50f;
public int accelTime = 600;
[SerializeField]
Rigidbody rb;
// Start is called before the first frame update
void Start()
{
startSpeed = _moveSpeed;
_controller = GetComponent<CharacterController>();
rb = GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
rb.velocity = new Vector3(0f, 0f, 1f) * _moveSpeed;
touchControls();
}
public void touchControls()
{
if (SwipeManager.swipeUp && _controller.isGrounded)
{
_directionY = _jumpspeed;
print("Entrato");
}
_directionY -= _gravity * Time.fixedDeltaTime;
_controller.Move((new Vector3(0, _directionY, 0)) * Time.fixedDeltaTime * Yspeed);
i += 1;
if (i % accelTime == 0)
{
_moveSpeed += startSpeed * speedAccel;
i = 0;
}
}
private void OnControllerColliderHit(ControllerColliderHit hit) //Checks if it collided with obstacles
{
if(hit.transform.tag == "Obstacle")
{
GameOverManager.gameOver = true;
}
}
}
You should either use the CharacterController or the Rigidbody. With the CharacterController you tell Unity where to place the character and you have to check yourself if it is a valid position. With the Rigidbody you push it in the directions you want, but the physics around the character is also interfering.
You can use Rigidbody.AddForce to y when jumping instead of _controller.Move.
I'm making an isometric 3D game. I made two joystick, one to move the player and the other one to shoot a projectile when the joystick is released. I made 3 attemps to achieve this result,but there's every time a problem. first try was with this :
clone.velocity = transform.TransformDirection(newpos);
but this need a rigidbody and a projectile can't be a rigidbody because it start from inside of the player.
Second try was this:
clone.transform.Translate(dir * (launchForce));
but this doesn't have a "speed" so it just move instantly to the position, not by moving, but translating
and the same happens with the third attemp:
clone.transform.position=Vector3.MoveTowards(Player.transform.position,newpos,10f);
This is the best solution until now because it gives me the possibility to choose a max range between the start posititon and the newposition.
Here's the full code:
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System.Collections;
public class shoot : MonoBehaviour, IDragHandler, IPointerUpHandler, IPointerDownHandler
{
private Image bgImg;
private Image joystickImg;
private Vector3 inputVector;
public GameObject proiettile;
private Vector3 dir = Vector3.zero;
private Vector3 newpos;
public float launchForce;
public Rigidbody Player;
private GameObject clone;
private void Start()
{
bgImg = GetComponent<Image>();
joystickImg = transform.GetChild(0).GetComponent<Image>();
}
public virtual void OnDrag(PointerEventData ped)
{
Vector2 pos;
if (RectTransformUtility.ScreenPointToLocalPointInRectangle(bgImg.rectTransform, ped.position, ped.pressEventCamera, out pos))
{
pos.x = (pos.x / bgImg.rectTransform.sizeDelta.x);
pos.y = (pos.y / bgImg.rectTransform.sizeDelta.y);
inputVector = new Vector3(pos.x * 2 +1, 0, pos.y * 2 - 1);
inputVector = (inputVector.magnitude > 1.0f) ? inputVector.normalized : inputVector;
// Move joystickImg
joystickImg.rectTransform.anchoredPosition =
new Vector3(inputVector.x * bgImg.rectTransform.sizeDelta.x / 3
, inputVector.z * (bgImg.rectTransform.sizeDelta.y / 3));
}
}
public virtual void OnPointerDown(PointerEventData ped)
{
OnDrag(ped);
}
public virtual void OnPointerUp(PointerEventData ped)
{
dir.x = Horizontal();
dir.z = Vertical();
newpos = dir * (launchForce);
clone = Instantiate(proiettile, Player.transform.position, Player.transform.rotation);
//third attempt
//clone.transform.position=Vector3.MoveTowards(Player.transform.position,newpos,10f);
//second attempt
//clone.transform.Translate(dir * (launchForce));
//first attempt
//clone.velocity = transform.TransformDirection(newpos);
// joystick come back to start position
inputVector = Vector3.zero;
joystickImg.rectTransform.anchoredPosition = Vector3.zero;
//temporary solution to replace the absence of a max range for projectile
clone.timeoutDestructor = 5;
}
public float Horizontal()
{
if (inputVector.x != 0)
return inputVector.x;
else
return Input.GetAxis("Horizontal");
}
public float Vertical()
{
if (inputVector.z != 0)
return inputVector.z;
else
return Input.GetAxis("Vertical");
}
}
If I were you I would use rigidbody attempt. Just make sure it does not collide with player by setting collision layers/disabling collider for first fraction of second/spawning projectile outside of player.
Why?
Two main reasons:
Rigidbody will handle interpolation for you out of the box (without it movement may be glitchy)
Rigidbody will handle "between frame" collisions with CCD for you out of the box (without it your projectile may go trough walls, or even targets if it's fast enough)
And these two features will save you a lot of time later
One of attempts:
When I spawn projectile I check with SphereCast (or whatever shape it has) if it's colliding with something. If it is I change isTrigger to true and then in "OnTriggerExit" I change isTrigger to false again. If it is not colliding with anything on spawn I just set isTrigger to false at start and that should do the trick.
I'm a Unity novice and I'm creating a small 2D shooting game on a school project and I already managed to have a functional shooting system in the main character that even bounces off some objects using Unity Physics.
I now wanted to integrate an aim line that will predict the trajectory of the bullet, including the bounces on objects (bubble-shooter style).
I found a script that uses Raycast and Line Renderer and it supposedly does this and I tried to integrate it into my gun script but although it does not give any error, it simply does not show anything when I test the game. I do not know if the problem is in the settings that I put in the Line Renderer Component or it is in the Script.
Could someone help me understand where my error is and point the right way?
My goal is:
My Line Renderer Component Definitions:
My Weapon Script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(LineRenderer))]
public class Weapon : MonoBehaviour
{
[Range(1, 5)]
[SerializeField] private int _maxIterations = 3;
[SerializeField] private float _maxDistance = 10f;
public int _count;
public LineRenderer _line;
public Transform Firepoint;
public GameObject BulletPrefab;
public GameObject FirePrefab;
void Start()
{
_line = GetComponent<LineRenderer>();
}
// Update is called once per frame
void Update()
{
if (Input.GetButtonDown("Fire1"))
{
Shoot();
}
_count = 0;
_line.SetVertexCount(1);
_line.SetPosition(0, transform.position);
_line.enabled = RayCast(new Ray(transform.position, transform.forward));
}
void Shoot()
{
//shooting logic
var destroyBullet = Instantiate(BulletPrefab, Firepoint.position, Firepoint.rotation);
Destroy(destroyBullet, 10f);
var destroyFire = Instantiate(FirePrefab, Firepoint.position, Firepoint.rotation);
Destroy(destroyFire, 0.3f);
}
private bool RayCast(Ray ray)
{
RaycastHit hit;
if (Physics.Raycast(ray, out hit, _maxDistance) && _count <= _maxIterations - 1)
{
_count++;
var reflectAngle = Vector3.Reflect(ray.direction, hit.normal);
_line.SetVertexCount(_count + 1);
_line.SetPosition(_count, hit.point);
RayCast(new Ray(hit.point, reflectAngle));
return true;
}
_line.SetVertexCount(_count + 2);
_line.SetPosition(_count + 1, ray.GetPoint(_maxDistance));
return false;
}
}
Unity has two physics engines one for 2D and one for 3D. The script you provided relies on the 3D physics engine and won't work for 2D colliders. I edited your script to function with 2D colliders.
Either way make sure that all your game objects use the same physics system. Also the original script only shows the line renderer if it hits something. Best of luck!
using UnityEngine;
[RequireComponent(typeof(LineRenderer))]
public class Weapon : MonoBehaviour
{
[Range(1, 5)]
[SerializeField] private int _maxIterations = 3;
[SerializeField] private float _maxDistance = 10f;
public int _count;
public LineRenderer _line;
public Transform Firepoint;
public GameObject BulletPrefab;
public GameObject FirePrefab;
private void Start()
{
_line = GetComponent<LineRenderer>();
}
// Update is called once per frame
private void Update()
{
if (Input.GetButtonDown("Fire1"))
{
Shoot();
}
_count = 0;
_line.SetVertexCount(1);
_line.SetPosition(0, transform.position);
_line.enabled = true;
RayCast(transform.position, transform.up);
}
private void Shoot()
{
//shooting logic
var destroyBullet = Instantiate(BulletPrefab, Firepoint.position, Firepoint.rotation);
Destroy(destroyBullet, 10f);
var destroyFire = Instantiate(FirePrefab, Firepoint.position, Firepoint.rotation);
Destroy(destroyFire, 0.3f);
}
private bool RayCast(Vector2 position, Vector2 direction)
{
RaycastHit2D hit = Physics2D.Raycast(position, direction, _maxDistance);
if (hit && _count <= _maxIterations - 1)
{
_count++;
var reflectAngle = Vector2.Reflect(direction, hit.normal);
_line.SetVertexCount(_count + 1);
_line.SetPosition(_count, hit.point);
RayCast(hit.point + reflectAngle, reflectAngle);
return true;
}
if (hit == false)
{
_line.SetVertexCount(_count + 2);
_line.SetPosition(_count + 1, position + direction * _maxDistance);
}
return false;
}
}
Well, I changed on the physics material of the bullet, the Friction to 0 and Bounciness to 1. Also on the rigidbody2D the linear drag, angular drag and gravity scale all to 0. Although it is not a perfect rebound it was very close to what I intend for the game. Thank you! Check it out: Game Gif
Only negative thing is that my bullet is not turning in the direction of the movement after the rebound. I tried to use transform.LookAt but didn`t work. This is my bullet script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Bullet : MonoBehaviour
{
public float speed = 20f;
public Rigidbody2D myRigidbody;
// Start is called before the first frame update
void Start()
{
this.myRigidbody = this.GetComponent<Rigidbody2D>();
this.myRigidbody.velocity = transform.right * speed;
transform.LookAt(transform.position + this.myRigidbody.velocity);
}
}
But now i have this error: Error CS0034 Operator '+' is ambiguous on operands of type 'Vector3' and 'Vector2'
UNITY 2D C#
I have a Canvas object.
My Canvas is image (ARROW) that indicates target (STAR).
When a STAR hit the PLAYER,it is destroyed.
Unfortunately, I can not turn off the ARROW and (when STAR respawn) turn on it again, because after appearing ARROW indicates the previous target.
That's why I must to destroy the canvas.
I added a script to the STAR :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DestroyTest : MonoBehaviour {
public SpawnStar other;
public GameObject Spawner;
public GameObject ToDestroy;
void Awake (){
GameObject Spawner = GameObject.Find ("Spawner");
other = Spawner.GetComponent<SpawnStar>();
}
void OnCollisionEnter2D (Collision2D coll){
if (coll.gameObject.tag == "Player") {
Destroy (gameObject);
Debug.Log("DestroyedStar");
GameObject ToDestroy = GameObject.Find ("Window_QuestPointer");
Destroy (ToDestroy);
Debug.Log("DestroyedOptionOne");
other.Start ();
}
}
}
I added a script to the CANVAS:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using CodeMonkey.Utils;
public class Window_QuestPointer : MonoBehaviour {
[SerializeField] private Camera uiCamera;
public SpawnStar other;
public GameObject Spawner;
private Vector3 targetPosition;
private RectTransform pointerRectTransform;
void Awake (){
GameObject Spawner = GameObject.Find ("Spawner");
other = Spawner.GetComponent<SpawnStar>();
other.Start ();
}
private void Start ()
{
targetPosition = GameObject.FindWithTag("Star").transform.position;
pointerRectTransform = transform.Find ("Pointer").GetComponent<RectTransform> ();
}
private void Update (){
Vector3 toPosition = targetPosition;
Vector3 fromPosition = Camera.main.transform.position;
fromPosition.z = 0f;
Vector3 dir = (toPosition - fromPosition).normalized;
float angle = UtilsClass.GetAngleFromVectorFloat(dir);
pointerRectTransform.localEulerAngles = new Vector3 (0, 0, angle);
float borderSize = 40f;
Vector3 targetPositionScreenPoint = Camera.main.WorldToScreenPoint (targetPosition);
bool isOffscreen = targetPositionScreenPoint.x <= borderSize || targetPositionScreenPoint.x >= Screen.width - borderSize || targetPositionScreenPoint.y <= borderSize || targetPositionScreenPoint.y >= Screen.height - borderSize;
Debug.Log (isOffscreen + " " + targetPositionScreenPoint);
if(isOffscreen){
Vector3 cappedTargetScreenPosition = targetPositionScreenPoint;
cappedTargetScreenPosition.x = Mathf.Clamp (cappedTargetScreenPosition.x, borderSize, Screen.width - borderSize);
cappedTargetScreenPosition.y = Mathf.Clamp (cappedTargetScreenPosition.y, borderSize, Screen.height - borderSize);
Vector3 pointerWorldPosition = uiCamera.ScreenToWorldPoint (cappedTargetScreenPosition);
pointerRectTransform.position = pointerWorldPosition;
pointerRectTransform.localPosition = new Vector3 (pointerRectTransform.localPosition.x, pointerRectTransform.localPosition.y, 0f);
}
else{
Vector3 pointerWorldPosition = uiCamera.ScreenToWorldPoint (targetPositionScreenPoint);
pointerRectTransform.position = pointerWorldPosition;
pointerRectTransform.localPosition = new Vector3 (pointerRectTransform.localPosition.x, pointerRectTransform.localPosition.y, 0f);
}
}
}
I added a script to the SPAWNER object:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpawnStar : MonoBehaviour {
private int waveNumber = 0;
public int enemiesAmount = 0;
public GameObject star;
public GameObject option;
public Camera cam;
public GameObject objectToEnable;
// Use this for initialization
public void Start () {
StartCoroutine (StarEnable());
cam = Camera.main;
enemiesAmount = 0;
objectToEnable.SetActive (false);
}
// Update is called once per frame
public IEnumerator StarEnable () {
yield return new WaitForSeconds (10f);
float height = cam.orthographicSize + 1; // now they spawn just outside
float width = cam.orthographicSize * cam.aspect + 1;
if (enemiesAmount==0) {
Instantiate(star, new Vector3(cam.transform.position.x + Random.Range(-width, width),3,cam.transform.position.z+height+Random.Range(10,30)),Quaternion.identity);
enemiesAmount++;
Instantiate (option, transform.position, transform.rotation);
objectToEnable.SetActive (true);
}
}
}
In addition, the ARROW must respawn in the screen and the STAR off the screen.
you shouldn't destroy a canvas or any GameObject during runtime, answering your question, when you Instantiate the canvas prefab, set the renderer camera with
canvas.worldCamera, but you can, instead of destroying your canvas, create a container inside of it put all the GameObjects inside of this GameObject container, and activate and deactivate it when needed.
If you have one camera U can attach simple script which finds Canvas commonent and attach camera to it in Start() for example:
public class AttachCamera: MonoBehavour
{
private void Start()
{
gameObject.GetComponent<Canvas>().worldCamera = Camera.main;
}
}
If there are more than one camera U have to remember attached camera and set it mannualy after Canvas was spawned.
Can you please show your code that is responsible for destroying the Canvas?
I would use another GameObject that handles this gameplay logic.
First, I would use cameraObj.SetActive(false); to disable the camera, and not destroy it. Destroying and Creating objects can cause memory issues sometimes, and it's just not good practice unless absolutely necessary.
Then, use a Coroutine and WaitForSeconds() or something to that effect to wait, and then call cameraObj.SetActive(true) to re-enable your Camera's main GameObject.
So i'm trying to change weapons for my 2d top down space shooter. by weapon I just mean my bullet prefab so it shoots a different bullet which I can then add damage to etc.
here is the code I have. when I press number 2 it shoots my prefab clone in the hierarchy but its greyed out and nothing shows up in the game view. Below is my playerShoot code.
public class playerShoot : MonoBehaviour {
public Vector3 bulletOffset = new Vector3 (0, 0.5f, 0);
float cooldownTimer = 0;
public float fireDelay = 0.25f;
public GameObject bulletPrefab;
int bulletLayer;
public int currentWeapon;
public Transform[] weapons;
void Start () {
}
void Update () {
if (Input.GetKeyDown(KeyCode.Alpha1)){
ChangeWeapon(0);
}
if (Input.GetKeyDown(KeyCode.Alpha2)){
ChangeWeapon(1);
}
cooldownTimer -= Time.deltaTime;
if (Input.GetButton("Fire1") && cooldownTimer <= 0){
cooldownTimer = fireDelay;
Vector3 offset = transform.rotation * bulletOffset;
GameObject bulletGO = (GameObject)Instantiate(bulletPrefab, transform.position + offset, transform.rotation);
bulletGO.layer =gameObject.layer;
}
}
public void ChangeWeapon(int num){
currentWeapon = num;
for (int i = 0; i < weapons.Length; i++){
if (i ==num)
weapons[i].gameObject.SetActive(true);
else
weapons[i].gameObject.SetActive(false);
}
}
}
Keeping the rest of the code same, just change the following line
GameObject bulletGO = (GameObject)Instantiate(bulletPrefab, transform.position + offset, transform.rotation);
to
GameObject bulletGO = (GameObject)Instantiate(weapons[currentWeapon].gameObject, transform.position + offset, transform.rotation);
What the change does is use the transforms for the weapons in your weapon array, rather than using the bulletPrefab.
The problem occured because you were Instantiating a single prefab, which was the same as the Transform you used for the first element in your weapons array. So, when you call ChangeWeapon(1), the prefab would get deactivated. This reulted in inactive GameObjects being instantiated.
What I would suggest you do is to have two separate prefabs and spawn those accordingly.