Way to fix bullet hole z-fighting? - c#

Trying to work myself through basic FPS shooting as practice and I want to have bullet holes where you shoot. However, Texture Z-fighting occurs when I attempt to do so.
The gun works using raycasting like so:
Physics.Raycast(playercam.transform.position, playercam.transform.forward, out hit)
The bullet holes are currently a 2d sprite that I place on the target using this script:
GameObject ceffect = Instantiate(bullethole, hit.point, Quaternion.LookRotation(hit.normal));
theoretically this should work, and it does, however, when it is placed on the world, it Z- fights with the floor texture.
I have tried to render the bullet holes on a different layer, but this simply does not work.
Is there a way to kind of pull the sprite to the top of the object?
Am I missing something with the layers?
If you need more code, here's my shooting script currently, but look at your own risk.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerShooting : MonoBehaviour
{
public float fireRate = 2f;
public float weapon = 1f;
public float pistoldmg = 5f;
public Camera playercam;
public GameObject redblood;
public GameObject bullethole;
private float timetofire = 0f;
void Start()
{
}
void Update()
{
if(Input.GetKeyDown(KeyCode.Mouse0) && (Time.time > timetofire))
{
timetofire = Time.time + 1f / fireRate;
PistolShoot();
}
else if(Input.GetKey(KeyCode.Mouse0))
{
}
}
void PistolShoot()
{
RaycastHit hit;
if(Physics.Raycast(playercam.transform.position, playercam.transform.forward, out hit))
{
Enemy1 enemy = hit.transform.GetComponent<Enemy1>();
if(enemy != null)
{
enemy.Shot(pistoldmg);
GameObject beffect = Instantiate(redblood, hit.point, Quaternion.LookRotation(hit.normal));
GameObject ceffect = Instantiate(bullethole, hit.point, Quaternion.LookRotation(hit.normal));
}
else
{
GameObject beffect = Instantiate(redblood, hit.point, Quaternion.LookRotation(hit.normal));
GameObject ceffect = Instantiate(bullethole, hit.point, Quaternion.LookRotation(hit.normal));
}
}
}
}
`

From the raycast hit information, we get the normal vector, which you're using to rotate the objects. We can use the same normal vector to 'nudge' the objects away from the hit point as well. An example piece of code might look something like this:
void PistolShoot ( )
{
if ( Physics.Raycast ( playercam.transform.position, playercam.transform.forward, out var hit ) )
{
if ( hit.transform.TryGetComponent<Enemy1> ( out var enemy ) )
enemy.Shot ( pistoldmg );
var rotation = Quaternion.LookRotation(hit.normal);
var beffect = Instantiate(redblood, hit.point + hit.normal * 0.01f, rotation );
var ceffect = Instantiate(bullethole, hit.point + hit.normal * 0.02f, rotation );
// .. are you using 'beffect' and 'ceffect' after this? There may not be a need to store these.
}
}
This will nudge the redblood object about "1 cm" away from the hit point, and the bullethole object about "2 cm" away from the hit point. This will likely need to be adjusted to suit your measurements, but if you've followed a one-to-one Unity unit to metre guide, then this will likely do the trick.
Note that some mobile phone use a lower precision when calculating positions, so you might need to increase this value to make it more pronounced.
Here's an example using a slightly exaggerated nudging amount - 0.05 and 0.1 instead of 0.01 and 0.02 - on a 1x1x1 cube.

Related

Preventing dash from going through walls using raycast

Currently, I want to make a dash for the player in a 2D game in unity. I'm using game objects to the left and right of the player to use raycasts to check the distance from a wall in a certain area. If its too close to a wall, I need it to dash only that distance (to the wall) instead of the full distance (behind/through the wall).
This is my code for dashing in my player movement (DashCheckLeft is -0.5 from the player) (DashCheckRight is 0.5 from the player):
public float dashDistance = 3f;
float distance;
public Transform DashCheckLeft;
public Transform DashCheckRight;
private void Update() {
if(Input.GetKeyDown(KeyCode.LeftShift)) {
Debug.Log("Dashed");
Dash();
private void FixedUpdate() {
RaycastHit2D hitLeft = Physics2D.Raycast(DashCheckLeft.position, Vector2.left, -dashDistance);
RaycastHit2D hitRight = Physics2D.Raycast(DashCheckRight.position, Vector2.right, dashDistance);
if(hitLeft.collider != null) {
distance = Mathf.Abs(hitLeft.point.x - transform.position.x);
}
else if(hitRight.collider != null) {
distance = Mathf.Abs(hitRight.point.x - transform.position.x);
}
else {
distance = dashDistance;
}
}
private void Dash() {
rb.position = new Vector2(rb.position.x + distance, rb.position.y);
}
The problem now is that apperently I'm only dashing the distance between the player and the DashChecks (0.5f) instead of the intended 3f and I believe it may be because the raycasts are hitting the collider of the player somehow, but changing my player to the "Ignore Raycast" layer makes it fall into the floor, and also doesn't fix the issue.
I worked on this problem, and found the answer. Basically, your raycasts were detecting the player's collider too. There is a simple fix for this. You should just add the fourth argument to your raycast. The fourth argument is a LayerMask. A LayerMask is like a tag, but you can have several on at the same time. This is what I changed the code to (I made some extra adjustments too):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
Rigidbody2D rb;
public float dashDistance = 3f;
public LayerMask floor;
float distance;
private void Start()
{
rb = GetComponent<Rigidbody2D>();
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.LeftShift))
{
RaycastHit2D left = Physics2D.Raycast(transform.position, -transform.right, dashDistance, floor);
RaycastHit2D right = Physics2D.Raycast(transform.position, transform.right, dashDistance, floor);
if (left.collider != null)
{
print("left");
distance = Mathf.Abs(left.point.x - transform.position.x);
distance = -distance;
}
else if (right.collider != null)
{
print("right");
distance = Mathf.Abs(right.point.x - transform.position.x);
}
else
{
distance = dashDistance;
}
print(distance);
Debug.Log("Dashed");
Dash();
}
void Dash()
{
rb.position = new Vector2(rb.position.x + distance, rb.position.y);
}
}
}
(when I say 'world objects', I mean the objects that make up the terrain. For example the ground or trees) You should add a new layer by clicking on the world objects. Then, you should look at the top of the inspector, then click the 'Layer Default' dropdown menu, and click the 'Add Layer...' button. Then write a new name on that list. Go back to the world game object, and click the same dropdown menu, and select the layer that you want for the world. You should make sure that all of the world objects have the same layer. Go back to the player object, and in the inspector, set the 'floor' variable to the same layer as the world. If that didn't make sense, here is a link of how to set layers.

How can I lock the rotation of a player in Unity?

I'm currently making a 2D game as a beginner and I made a spinning platform. But when it's rotating the player's rotation (z-axis) also changes because it's a child of the platform. I need this when I use moving platforms. Now I want to lock the z-axis of the rotation of the player. I already tried it in 3 different ways, but none of them seems to be working. Does anybody know how to do this?
These are the three ways I tried:
// 1
PlayerTrans.transform.Rotate(
PlayerTrans.transform.rotation.x,
PlayerTrans.transform.rotation.y,
0);
// 2
PlayerTrans.transform.Rotate(
PlayerTrans.transform.rotation.x,
PlayerTrans.transform.rotation.y,
0,
Space.Self);
// 3
PlayerTrans.transform.localRotation = Quaternion.Euler(new Vector3(
PlayerTrans.transform.localEulerAngles.x,
PlayerTrans.transform.localEulerAngles.y,
0f));
and this is, what my code looks like for staying on the moving platforms. I used raycasting for this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Raycasting : MonoBehaviour
{
// Start is called before the first frame update
Transform PlayerTrans;
public float RayCastRange = 3;
void Start()
{
PlayerTrans = transform.parent;
}
// Update is called once per frame
void Update()
{
RaycastHit2D PlattformCheck = Physics2D.Raycast(transform.position, -Vector2.up, RayCastRange);
if (PlattformCheck.collider != null)
{
if (PlattformCheck.collider.gameObject.tag == "Platform")
{
PlayerTrans.transform.SetParent(PlattformCheck.collider.gameObject.transform);
}
else
{
PlayerTrans.transform.SetParent(null);
}
}
else
{
PlayerTrans.transform.SetParent(null);
}
}
}
There are 2 ways that might help you:
Just freeze the rotation from the inspector:
you can use some LookAt function (there is one for 3D but you can look and find ones for 2D) and just look at the camera.
(if you cant find it let me know and I will add it)
You should raycast directly down and then apply velocities to both objects (un-child the player from the platforms). You could do something like this for the player:
public LayerMask mask; //set ‘mask’ to the mask of the
//platform in the Unity Editor.
public Vector3 velocity;
void Update()
{
RaycastHit hit;
if (Physics.Raycast(transform.position, -Vector3.up, out hit, 0.1f, mask))
//0.1f is the distance to the platform to be able to be moved by the platform.
{
velocity = hit.collider.gameObject.GetComponent<Platform>().velocity;
}
float h = Input.GetAxis(“Horizontal”);
//this one is for CharacterController:
cc.Move(velocity);
//this one is for rigidbody:
rb.velocity = velocity;
velocity = 0;
}
It takes the velocity from the ‘Platform’ script and moves the player based on it. Now we should add the platform script. Call it ‘Platform’.
public Vector3 velocity;
public Vector3 a; //a and b are the two points you want the platform to go between.
public Vector3 b;
public float period = 2f; //the amount of seconds per cycle.
float cycles;
void Update()
{
cycles = Time.time / period;
float amplitude = Mathf.Sin(Mathf.PI * 2f * cycles);
Vector3 location = Vector3.Lerp(a, b, amplitude);
velocity = transform.position - location;
transform.position = velocity;
}
This script interpolates between the point a and b, then it finds the velocity and applies it. The player takes this velocity and it moves the player. Comment if you found an error.

How to make a ground object follow a flying object in unity?

I'm trying to make an object on the ground follow a flying object, for example a drone leading a human (for now i'm just using shapes - a cube and a capsule). My cube follows the capsule like i desire but i want the cube to follow the capsule on the ground only, rather than go up on the y-axis with the capsule. Right now, it follows the capsule everywhere, I want the capsule to lead while the cube follows along on the ground.
I have done some research on Google and Youtube but I have not seen any results. Please let me know how I can achieve this.
This is the code script attached to the cube(ground object)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class follow_target : MonoBehaviour
{
public Transform mTarget;
float mSpeed = 10.0f;
const float EPSILON = 0.1f;
Vector3 mLookDirection;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
mLookDirection = (mTarget.position - transform.position).normalized;
if((transform.position - mTarget.position).magnitude > EPSILON)
transform.Translate(mLookDirection * Time.deltaTime * mSpeed);
}
}
If the ground is planar, you can just set the y component to 0 (or whatever the ground y vector is).
If the ground changes in topology, you can do a raycast down from the capsule to get the hit point (vector3). You can use the hit point y component for the height. After that you will need to set the cubes rotation so that it is aligned to the ground. You could do that with a raycast as well, there are a number of examples of that online.
I hope that helps get you in the right direction.
Assuming a flat ground on Y = 0
either make sure your objects sticks to the ground so set
private const float EPSILONSQR = EPSILON * EPSILON;
void Update()
{
var difference = mTarget.position - transform.position;
mLookDirection = difference.normalized;
if(difference.sqrmagnitude > EPSILONSQR)
{
// In general be aware that Translate by default moves in the
// objects own local space coordinates so you probably would rather
// want to use Space.World
transform.Translate(mLookDirection * Time.deltaTime * mSpeed, Space.World);
var pos = transform.position;
// reset the Y back to the ground
pos.y = 0;
transform.position = pos;
}
}
or simply already map the direction down on the XZ plane (ignoring any difference in Y) like
private const float EPSILONSQR = EPSILON * EPSILON;
void Update()
{
var difference = mTarget.position - transform.position;
mLookDirection = difference.normalized;
// simply ignore the difference in Y
// up to you if you want to normalize the vector before or after doing that
mLookDirection.y = 0;
if(difference.sqrmagnitude > EPSILONSQR)
{
transform.Translate(mLookDirection * Time.deltaTime * mSpeed, Space.World);
}
}

Object kept falling as opposed to shooting trajectory (Unity)

everyone! I am new to Unity and try to create a 2D arcade game by allowing my character to jump up at a trajectory motion to reach something then fall down. I use gravity scale equal to 1 and move_speed 1000 and rest_speed 500 but as soon as I hit space, the character just fell down instead of going up.
Also, walk.IsWalk is an int from Walking class(as soon as I hit right arrow, set IsWalk to 2 for example). My expected result would be as soon as we hold both space and right arrow key, the character should move upwards follow a curved motion. I dont know what is going on here, can anyone kindly point me to a direction? Thanks!
My code as follows:
using UnityEngine;
using System.Collections;
public class Jumping : MonoBehaviour
{
Rigidbody2D rb2D;
Animator anim;
Walking walk = new Walking();
public float move_speed;
public float rest_speed;
void Start()
{
rb2D = GetComponent<Rigidbody2D>();
anim = GetComponent<Animator>();
}
void FixedUpdate()
{
//float xmove = Input.GetAxis("Horizontal");
//float ymove = Input.GetAxis("Vertical");
//var curr = new Vector2(xmove, ymove);
if (Input.GetKey("space"))
{
anim.SetTrigger("JumpButtonPres");
rb2D.gravityScale = 1;
if (walk.isWalk == 2)
{
rb2D.AddForce(rb2D.position * move_speed);
}
else if (walk.isWalk == 1)
{
//rb2D.velocity += (Vector2.up * (-rb2D.velocity.x));
rb2D.AddForce(-rb2D.position * move_speed);
}
else
{
rb2D.AddForce(rb2D.position * rest_speed);
}
}
}
}
partial code for rigidbody2D.AddForce
It seems your problem is that whenever you are adding force to your rigidbody, you are multiplying it by the position where it's located, so by this i'm guessing your character is placed at a negative y component coordenate, and this is why rb2D.position * rest_speed will result on your playing falling. Change it for vector2.up * rest_speed and you should be fine.
Hope I was able to help.
Noé

Unity3D: Get Velocity after collision

I'm making a billiard game and I have two questions:
How do I find the velocity of two balls when they collide with each other and how do I apply it to both balls ?
I already know the angles that they're gonna move, I just need to find the velocity that they'll move in those directions.
I was never good at physics/physics programming so I would appreciate any help given! Btw, my game is in 3D.
Thank you for your time!
Edit: What I'm trying to do is make the movement direction match the direction that I'm calculating using this script:
if (Physics.Raycast(ray, out hit, Mathf.Infinity, lmm))
{
location = new Vector3(hit.point.x, 1.64516f, hit.point.z);
}
if (Physics.SphereCast(transform.position, 0.77f, location - transform.position, out hitz, Mathf.Infinity, lmm2))
{
if (hitz.collider.tag == "Ball")
{
Vector3 start = hitz.point;
end = start + (-hitz.normal * 4);
lineRenderer2.SetPosition(1, end);
}
}
You can calculate the new velocities by applying an impulse to each ball. We can apply Newton's Third law to do so. PseudoCode:
RelativeVelocity = ball1.velocity - ball2.velocity;
Normal = ball1.position - ball2.position;
float dot = relativeVelocity*Normal;
dot*= ball1.mass + ball2.mass;
Normal*=dot;
ball1.velocity += Normal/ball1.mass;
ball2.velocity -= Normal/ball2.mass;
This however does not take into account friction between the two balls nor their angular momentum.
If I understand correctly, what you are trying to do is to apply the speed of "Ball A" as a force to "Ball B", and vice versa as well. If that is the case I would suggest something like this:
Attach this script to all the balls:
public class BallCollisionScript : MonoBehaviour
{
public Rigidbody Rigid_Body;
void Start()
{
Rigid_Body = this.GetComponent<Rigidbody>();
}
void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.tag.Equals("Ball"))
{
BallScript otherBall = collision.gameObject.GetComponent<BallScript>();
Rigid_Body.AddForce(otherBall.Rigid_Body.velocity);
}
}
}
Make sure that all balls are tagged with "Ball".

Categories