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'
Related
I was making a platform that bounces the player after a certain period of time when the player touches it. I attached this code component to the GameObject, which is a tilemap, and it doesn't work properly. What's wrong with my code? The code and scriptable object are as follows.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ExplosionPlatform : PlatformBase
{
public float explosionPower = 20f;
public float waitTime = 1.5f;
public float colTime = 0f;
public bool isDamageOnce = false;
public Vector2 area = new Vector2(2, 2);
public Vector2 positionModify = new Vector2(1, 0);
private void OnCollisionEnter2D(Collision2D collision)
{
StartCoroutine(Explosion());
}
private IEnumerator Explosion()
{
Debug.Log("ASDF");
yield return new WaitForSeconds(waitTime);
Collider2D[] player = Physics2D.OverlapBoxAll(transform.position + (Vector3)positionModify, area, 0);
foreach (Collider2D col in player)
{
if (col.CompareTag("Player"))
{
col.gameObject.GetComponent<Rigidbody2D>().AddForce(Vector2.up * explosionPower, ForceMode2D.Impulse);
Damage();
isDamageOnce = true;
yield return new WaitForSeconds(3f);
isDamageOnce = false;
}
}
}
private void OnDrawGizmos()
{
Gizmos.color = Color.blue;
Gizmos.DrawWireCube(transform.position + (Vector3)positionModify, area);
}
}
I have confirmed that the code below works properly on GameObject, which is Unity 2d Sprite. Is there any way to use that code while using the tile map?
OK, I have a particle which is prefabed as it appears on the left of the image below.
The left side is the result of the projectile code:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(Collider2D))]
[RequireComponent(typeof(Rigidbody2D))]
[RequireComponent(typeof(SpriteRenderer))]
public class Projectile : MonoBehaviour
{
[SerializeField] float velocity;
[SerializeField] float acceleration;
new Collider2D collider;
Rigidbody2D rb;
SpriteRenderer sr;
[SerializeField] LayerMask targetLayer;
[SerializeField] float lifetime;
[SerializeField] float damage;
[SerializeField] ParticleSystem HitEffect;
public event Action<Projectile,Collider2D> OnHit;
public Color Color
{
get
{
return sr.color;
}
set
{
sr.color = value;
}
}
// Start is called before the first frame update
void Start()
{
collider = GetComponent<Collider2D>();
rb = GetComponent<Rigidbody2D>();
sr = GetComponent<SpriteRenderer>();
collider.isTrigger = false;
}
// Update is called once per frame
void Update()
{
lifetime -= Time.deltaTime;
if (lifetime <=0)
{
Destroy(this.gameObject);
}
}
public void SetTargetMask(LayerMask _mask)
{
targetLayer = _mask;
}
public void SetDamage(float amount)
{
this.damage = amount;
}
protected virtual void OnCollisionEnter2D(Collision2D other)
{
var _hitEffect = Instantiate(HitEffect.gameObject, transform).GetComponent<ParticleSystem>();
if (!_hitEffect.isPlaying) _hitEffect.Play();
rb.velocity = Vector2.zero;
collider.isTrigger = true;
sr.enabled = false;
Damageable othersDamage;
other.gameObject.TryGetComponent<Damageable>(out othersDamage);
//Debug.Log("dmg not null: " + (othersDamage != null) + "; presumed layer:" + other.gameObject.layer + "; actual layer:" + targetLayer.value);
if (othersDamage != null && other.gameObject.layer == targetLayer.value)
{
OnHit?.Invoke(this,other.collider);
othersDamage.HandleDamage(damage);
}
Destroy(this.gameObject,HitEffect.main.duration);
}
}
So, the idea is to have the particle as it appears on the left at each contact point, play, and vanish.
Instead, I have a non-animated particle that floats off after hitting the target.
What am I missing to get to play the single explosion at point, and how to get the sub particle to play properly.
What am I missing to get to play the single explosion at point
Either stop the rigidbody completely:
rb.angularVelocity = Vector2.zero;
rb.velocity = Vector2.zero;
Alternately, unparent the hit effect from the projectile, and destroy it after a delay
_hitEffect.transform.SetParent(null);
Destroy(_hitEffect.gameObject,afterDelay);
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.
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.
i'm new to unity and i'm trying to make an endless runner. When my player (a ball) hits one of the walls the game needs to go the scene: LostMenu. My problem is that the collision doesn't work. Nothing happens when they collide... Here are the inspectors on both the player and the walls: Walls http://prntscr.com/a2pgzm Player http://prntscr.com/a2ph66 .
The collision script on the walls:
using UnityEngine;
using System.Collections;
public class LostByWallCSR : MonoBehaviour
{
void OnCollisionEnter (Collision col)
{
if(col.gameObject.name == "Player")
{
Application.LoadLevel("LostMenu");
Debug.Log ("WORKS!");
}
}
}
The movement script on the player:
using UnityEngine;
using System.Collections;
public class CharacterControllerz : MonoBehaviour {
public float speed = 5f;
public float gravity = 20f;
private Vector3 moveDirections = new Vector3();
private Vector3 inputs = new Vector3();
void FixedUpdate()
{
CharacterController cc = GetComponent<CharacterController>();
if (cc.isGrounded)
{
if (Input.GetKey("left"))
inputs.z = 3;
else if (Input.GetKey("right"))
inputs.z = -3;
else
inputs.z = 0;
moveDirections = transform.TransformDirection(inputs.x, 0, inputs.z) * speed;
}
inputs.x = 5;
moveDirections.y = inputs.y - gravity;
cc.Move(moveDirections * Time.deltaTime);
}
}
Any idea on how to setup the collision properly? Attaching a rigibody is not helping it. Unless i'm missing something ofcourse.
Make sure both objects have a collider component.
You need to attach a rigidbody component to one of the objects.(if you don't attach rigid body it will not work)
your Sphere collider is Trigger , remove that or change your function to
public void OnTriggerEnter(Collider other)
{
}
You need to use OnControllerColliderHit method
private void OnControllerColliderHit(ControllerColliderHit hit)
{
if (hit.gameObject.CompareTag("Trap"))
{
Debug.Log("colisiono with trap");
playerDentro = true;
}
else
{
if (!hit.gameObject.CompareTag("Trap"))
{
Debug.Log("player salio de la trampa");
playerDentro = false;
}
}
}e