I have this issue and I can't fix it. I've already tried to search on internet but I found nothing.
The "projectile spawn" is situated next to my character. When I left-click on my mouse, the projectile starts shooting going forward (in the Z-axis at 20 speed, for example) but there is this issue that is making me mad. Sometimes it starts from the right place, sometimes under and under every click.
public Rigidbody projectile;
public float speed = 20;
private Transform spawnPoint;
private int SPAWN_DISTANCE = 1;
void Awake()
{
spawnPoint = transform.Find("SpawnPoint");
}
void Update ()
{
//
if (Input.GetButtonDown("Fire1"))
{
atkSpeed = 0.2f;
Shoot(projectile);
attackTime = atkSpeed;
chargeLevel = 0;
}
//
}
void Shoot(Rigidbody proj){
Rigidbody _projBody = Instantiate(proj, spawnPoint.position + SPAWN_DISTANCE * transform.forward, transform.rotation) as Rigidbody;
_projBody.velocity = transform.TransformDirection(new Vector3(0, 0,speed));
}
Video of what is happening: https://www.youtube.com/watch?v=GLgrfl2zU9w
EDIT: I SOLVED MYSELF YESTERDAY.
Put an Empty GameObject where you want the projectile to spawn from. Nest it inside the character that fires.
Then find the SpawnPoint.
private Transform spawnPoint;
private int SPAWN_DISTANCE = 1;
void Awake()
{
spawnPoint = transform.Find("SpawnPoint");
}
Also make this change to your shooting.
void Shoot()
{
Rigidbody _projBody = Instantiate(projectile, spawnPoint.position + SPAWN_DISTANCE * transform.forward, transform.rotation) as Rigidbody;
_projBody.velocity = transform.TransformDirection(new Vector3(0, 0,speed));
}
Related
I am making a 2D platforming game in unity. For some reason, the player is getting stuck in the wall. I have used a Tilemap for the wall, then used tilemap Collider 2D with composite collider. The same thing works fine with the ground.
When I go left facing the wall, it behaves as expected:
When I release the left arrow button, it also shows the expected result:
But when I click right arrow next, the player gets stuck inside the wall:
and cannot get out:
Here is my player movement code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class playerScript : MonoBehaviour
{
// Start is called before the first frame update
[SerializeField] private float movement_speed = 2;
[SerializeField] private float jump_power = 5;
[SerializeField] private LayerMask groundLayer;
[SerializeField] private LayerMask wallLayer;
private Rigidbody2D rb_player;
private BoxCollider2D boxCollider;
private Animator anim;
private void Start()
{
}
private void Awake()
{
rb_player = GetComponent<Rigidbody2D>();
anim = GetComponent<Animator>();
boxCollider = GetComponent<BoxCollider2D>();
groundLayer = LayerMask.GetMask("ground");
wallLayer = LayerMask.GetMask("wall");
}
// Update is called once per frame
private void Update()
{
float x = Input.GetAxis("Horizontal");
rb_player.velocity = new Vector2(x * movement_speed, rb_player.velocity.y);
//flip the player
if(x > 0.01f)
{
transform.localScale = new Vector3(1, 1, 1);
}
else if(x < -0.01f)
{
transform.localScale = new Vector3(-1, 1, 1);
}
if(Input.GetKeyDown(KeyCode.Space) && isGrounded())
{
jump();
}
anim.SetBool("run", x != 0);
anim.SetBool("jump", !isGrounded());
print("wall: "+isTouchingWall());
print("ground: "+isGrounded());
}
private void jump()
{
rb_player.velocity = new Vector2(rb_player.velocity.x, jump_power);
anim.SetTrigger("jump_trigger");
}
private void OnCollisionEnter2D(Collision2D collision)
{
// if(collision.gameObject.tag == "ground")
// {
// grounded = true;
// }
}
private bool isGrounded()
{
RaycastHit2D raycastHit = Physics2D.BoxCast(boxCollider.bounds.center, boxCollider.bounds.size, 0, Vector2.down, .2f, groundLayer);
return raycastHit.collider != null;
}
private bool isTouchingWall()
{
RaycastHit2D raycastHit = Physics2D.BoxCast(boxCollider.bounds.center, boxCollider.bounds.size, 0, new Vector2(transform.localScale.x,0), .1f, wallLayer);
return raycastHit.collider != null;
}
}
It would be nice if anyone can help me as I am a complete beginner in game development with unity.
The problem
Your pivot point of your player (where your player script is attached) isn't centered like your collider. Because of this, your box collider is being flipped like a page from a book and it lands inside the wall.
To fix it
Make sure that your player pivot point is the same as the center of your BoxCollider2D. This way, your collider won't "teleport" from right to left and bypass walls.
Other tips
I see some beginner's mistakes in your code, so even if you manage to fix your issue, I highly recommend to follow the next tips:
All physic based code (jump, movement, etc.) should go inside FixedUpdate() method and use Time.deltaTime or Time.fixedDeltaTime to keep the same speed (for movement and jump) among computers.
Use the proper naming conventions, your variables should be camel case, no underscores. Methods (and Classes) should always start with a capital letter in C# (PascalCase).
Hopefully this will help you.
my GameObject (Rocket) does fly up when I start the level (Its a 2D Game).
Now I want code it so, that my Rocket will do something when it will arrive at the position for example (0,0,0) but it does not start at 0 it starts something like (0,-15,0).
I tried to code that but it does not work, I checked already that it arrives for 100% at (0,0,0) but I dont understand why it does not run my code when this happen.
My Code:
public class Flying : MonoBehaviour
{
public float speed = 5f;
private void FixedUpdate()
{
transform.position += new Vector3(0, 1, 0) * (Time.deltaTime * speed);
//Check if my Rocket arrives at (0,0,0)
if (transform.position == new Vector3(0,0,0))
{
Debug.Log("I'm here!");
speed = 3f;
}
}
}
Any idea how to solve that?
I solve the problem with another GameObject that is invisible and have Collider2D on it with Rigidbody2D. Now when my Rocket touches the invisible GameObject the speed will change.
I hoped that I could code that easier.
My final code:
public class Flying : MonoBehaviour
{
public float Speed = 5f;
private void FixedUpdate()
{
transform.position += new Vector3(0, 1, 0) * (Time.deltaTime * Speed)
}
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.collider.name == "Slow")
{
Speed = 3f;
}
}
}
Im working on a first person shooter. I have an aim function, which puts the pistol right in front of the camera, to make it look like your holding it in front of you. Im trying to make it so the pistol will also rotate with the camera on the Z axis, so that way the pistol wont stay still, because that looks odd and gets in the way. To do this, I tried this:
GPR.gun.transform.rotation = Quaternion.Euler(0, 0, plrCam.transform.rotation.z);, however this ends up rotating the gun very slightly around the z axis, and mainly rotating it around the y axis whenever I move my camera. I am a beginner programmer in Unity so please try to make answers more digestible to beginners so I can understand it. Here is my full script:
using System.Collections.Generic;
using UnityEngine;
public class PistolFire : MonoBehaviour
{
//Gun Properties
public float range = 50f;
public float damage = 10f;
//Sensitivity decrease for looking down the sights
public float downSights = 5f;
//Other vars
private playerGunControls playerGun;
private GameObject plrCam;
private Camera fpsCam;
private ParticleSystem muzzleFlash;
private GameObject impactEffect;
private bool aimed = false;
private GameObject aimPos;
private GunPickupRaycast GPR;
private GameObject handPos;
private GameObject Player;
// Start is called before the first frame update
void Start()
{
//Getting objects because gun is instantiated, so this is necessary
plrCam = GameObject.Find("Player Camera");
playerGun = plrCam.GetComponent<playerGunControls>();
fpsCam = plrCam.GetComponent<Camera>();
muzzleFlash = GetComponentInChildren<ParticleSystem>();
impactEffect = GameObject.Find("Impact Effect");
aimPos = GameObject.Find("aimPos");
GPR = plrCam.GetComponent<GunPickupRaycast>();
handPos = GameObject.Find("Hand Pos");
Player = GameObject.Find("Player");
}
// Update is called once per frame
void Update()
{
//Check for shoot button down
if (Input.GetButtonDown("Fire1"))
{
if (playerGun.holding == "Pistol")
{
Shoot();
}
}
//Check if aim button down
if (Input.GetButton("Fire2"))
{
if (playerGun.holding == "Pistol")
{
Aim();
}
}
//Check if no longer aiming to reset to normal
if (aimed == true && !(Input.GetButton("Fire2")))
{
Unaim();
}
}
void Shoot()
{
muzzleFlash.Play();
RaycastHit hit;
if(Physics.Raycast(plrCam.transform.position, plrCam.transform.forward, out hit, range))
{
Debug.Log(hit.transform.name);
Health health = hit.transform.GetComponent<Health>();
if (health != null)
{
health.TakeDamage(damage);
}
//Instantiate the Impact Effect
GameObject IE = Instantiate(impactEffect, hit.point, Quaternion.identity);
Destroy(IE, 1.5f);
}
}
void Aim()
{
aimed = true;
Debug.Log("Aiming");
GPR.gun.transform.position = aimPos.transform.position;
GPR.gun.transform.rotation = Quaternion.Euler(0, 0, plrCam.transform.rotation.z);
}
void Unaim()
{
GPR.gun.transform.position = handPos.transform.position;
Debug.Log("No longer aiming");
aimed = false;
}
}
I fixed my problem by making the gun a child of my camera instead of a child of my player.
I have a script that spawns my ground prefabs infinitely. The only problem is that occasionally there is a "bump" for the player to hit at the boundary of a new ground prefab.
It seems like one prefab is slightly higher than the previous one and when the player hits that small bump they go flying. How do I fix this?
I might just make the player a game object instead of a rigidbody and animate it instead of using actual physics.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GroundManager : MonoBehaviour
{
//oH is object height
public GameObject[] ground;
public GameObject[] obstacles;
private Transform playerTransform;
private float spawnZ = 0.0f;
private float spawnO = 0.0f;
public float oH;
private float tileLength = 40.0f;
private float oLength = 36.0f;
private int tilesOnScreen = 8;
void Start()
{
playerTransform = GameObject.FindGameObjectWithTag("Player").transform;
for (int i = 0; i < tilesOnScreen; i++)
{
SpawnTile();
SpawnObstacle();
}
}
// Player must be tagged "Player"
void Update()
{
if (playerTransform.position.z > (spawnZ - tilesOnScreen * tileLength))
{
SpawnTile();
SpawnObstacle();
}
}
private void SpawnTile(int prefabIndex = -1)
{
GameObject go;
go = Instantiate(ground[0]) as GameObject;
go.transform.SetParent(transform);
go.transform.position = Vector3.forward * spawnZ;
spawnZ += tileLength;
}
private void SpawnObstacle()
{
GameObject go;
go = Instantiate(obstacles[Random.Range(0, obstacles.Length)]) as GameObject;
go.transform.SetParent(transform);
go.transform.position = new Vector3(0, oH, 1 * spawnO);
spawnO += oLength;
}
}
This code works to infinitely spawn the ground objects but has the bumps that I described. Just one bump is enough to screw up the whole game.
I stumbled across a problem while working with the book "Unity in Action". At the end of chapter 3 you'd end up with the basics for a simple fps game. It's basically a player (camera attached to it) in a simple and small level which only exists out of a number of cubes forming walls and the floor etc. These cubes all have box collider on them. Now the player is also able to shoot at moving enemies which are also able to shoot. This was done by Raycast/RaycastHit. All this worked perfectly so I wanted to add something that reassembles bullet holes, simply by instantiating a black sphere object on the wall where the fireball (this is the object the enemy and the player shoot) hits it. This works BUT sometimes the fireball object just goes through the wall. In case there is another wall behind it, the fireball object gets destroyed by this second wall and the desired sphere is created on the second wall instead of on the first wall.
I changed the speed in the fireball from 20 to 10 and then back to 20 and at a speed of 10, the success rate is around 19/20, while at a speed of 20 it's around 6/10.
Here is the code of the fireball which is supposed to check whether it hits the player (then health is deducted, that works fine) or hits the enemy (then the enemy falls over, also works fine) or hits the wall in which case the sphere should be created.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Fireball : MonoBehaviour {
public float speed = 10.0f;
public int damage = 1;
[SerializeField] private GameObject wallHit;
private GameObject _wallHit;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
transform.Translate(0,0, speed * Time.deltaTime);
}
void OnTriggerEnter(Collider other){
RaycastHit hit;
PlayerCharacter player = other.GetComponent<PlayerCharacter>();
ReactiveTarget target = other.GetComponent<ReactiveTarget>();
WallBehavior wall = other.GetComponent<WallBehavior>();
if(player != null){
player.Hurt(damage);
}
if(target != null){
target.ReactToHit();
}
if(wall != null){
if(Physics.Raycast(transform.position, transform.forward, out hit)){
wall.WallWasHit(hit);
}
}
Destroy(this.gameObject);
}
}
As you can see one thing I tried was to give each wall a WallBehavior script, which looks like this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class WallBehavior : MonoBehaviour {
[SerializeField] private GameObject wallHit;
private GameObject _wallHit;
static int count = 0;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
public void WallWasHit(RaycastHit hit){
count++;
Debug.Log("Wall was hit: " + count);
_wallHit = Instantiate(wallHit) as GameObject;
_wallHit.transform.position = hit.point;
}
}
But none of my attempts to understand why this happens so infrequently was successful so far and I hope someone can help me with this because I feel like might be crucial for my learning before I continue with the book! Thanks in advance. if further information are needed, I am happy to provide them. See the following picture for a better visualization of the issue.
EDIT:
As mentioned in the answers, I replaced the fireball script accordingly but I am not sure how to change the following script likewise. This script is on my camera which is on my player object. In the Update function the Fireball is instantiated and also given a movement, so I guess this causes the problems?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RayShooter : MonoBehaviour {
private Camera _camera;
[SerializeField] private GameObject fireballPrefab;
private GameObject _fireball;
void Start () {
_camera = GetComponent<Camera>();
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
}
void OnGUI(){
int size = 12;
float posX = _camera.pixelWidth/2 - size/4;
float posY = _camera.pixelHeight/2 - size/2;
GUI.Label(new Rect(posX, posY, size, size), "X");
}
// Update is called once per frame
void Update () {
if(Input.GetMouseButtonDown(0)){
Vector3 point = new Vector3(_camera.pixelWidth/2, _camera.pixelHeight/2, 0);
_fireball = Instantiate(fireballPrefab) as GameObject;
_fireball.transform.position = transform.TransformPoint(Vector3.forward * 1.5f);
_fireball.transform.rotation = transform.rotation;
Ray ray2 = _camera.ScreenPointToRay(point);
RaycastHit hit;
if(Physics.Raycast(ray2, out hit)){
GameObject hitObject = hit.transform.gameObject;
ReactiveTarget target = hitObject.GetComponent<ReactiveTarget>();
if(target !=null){
target.ReactToHit();
}
}
}
}
}
This is not how to move a Rigidbody Object and moving it like this could cause so many issues including the one mentioned in your question. Objects with Rigidbody should be moved with the Rigidbody component and with the functions like Rigidbody.MovePosition, Rigidbody.AddForce and Rigidbody.velocity not by the transform or transform.Translate.
Also, you should move the Rigidbody Object in the FixedUpdate function instead of the Update function. If you are moving your other Rigidbody Objects by their transform then you must fix them too. The example below replaces transform.Translate with Rigidbody.MovePosition in the Fireball script:
public float speed = 10.0f;
public int damage = 1;
[SerializeField]
private GameObject wallHit;
private GameObject _wallHit;
public Rigidbody rb;
// Use this for initialization
void Start()
{
rb = GetComponent<Rigidbody>();
}
// Update is called once per frame
void FixedUpdate()
{
//Move to towards Z-axis
Vector3 pos = new Vector3(0, 0, 1);
pos = pos.normalized * speed * Time.deltaTime;
rb.MovePosition(rb.transform.position + pos);
}
void OnTriggerEnter(Collider other)
{
RaycastHit hit;
PlayerCharacter player = other.GetComponent<PlayerCharacter>();
ReactiveTarget target = other.GetComponent<ReactiveTarget>();
WallBehavior wall = other.GetComponent<WallBehavior>();
if (player != null)
{
player.Hurt(damage);
}
if (target != null)
{
target.ReactToHit();
}
if (wall != null)
{
if (Physics.Raycast(transform.position, transform.forward, out hit))
{
wall.WallWasHit(hit);
}
}
Destroy(this.gameObject);
}
If you still run into issues, use Rigidbody.velocity instead:
void FixedUpdate()
{
Vector3 pos = Vector3.zero;
pos.z = speed * Time.deltaTime;
rb.velocity = pos;
}
Sometimes, depending on the size of the object and the speed it is moving by, you many need to set its Rigidbody Interpolate from None to Interpolate and Collision Detection to Continuous.
Don't forget to remove the code in the Update function.
EDIT:
I looked at your project and found new problems:
1.You enabled IsTrigger on your "Fireball" GameObject. Please uncheck the IsTrigger on the collider.
2.You just want to shoot a bullet. The force should be added once only not every FixedUpdate. Add the force in the Start function instead of the FixedUpdate function. Use Rigidbody.velocity instead of Rigidbody.MovePosition.
Remove the FixedUpdate function and the code inside it.
This should be your new Start function:
void Start()
{
rb = GetComponent<Rigidbody>();
Vector3 pos = transform.forward * speed * Time.deltaTime;
rb.velocity = pos;
}