I'm trying to make a pong game where the player has to move both paddles. They are currently set as dynamic 2D Rigidbodies(collision detection continuous) and have a box collider (is NOT trigger) attached.
The problem is the paddles won't collide with the surrounding box colliders I set on the camera, using the following script :
using UnityEngine;
using System.Collections;
namespace UnityLibrary
{
public class EdgeCollider : MonoBehaviour
{
public float colDepth = 4f;
public float zPosition = 0f;
private Vector2 screenSize;
private Transform topCollider;
private Transform bottomCollider;
private Transform leftCollider;
private Transform rightCollider;
private Vector3 cameraPos;
// Use this for initialization
void Start () {
//Generate our empty objects
topCollider = new GameObject().transform;
bottomCollider = new GameObject().transform;
rightCollider = new GameObject().transform;
leftCollider = new GameObject().transform;
//Name our objects
topCollider.name = "TopCollider";
bottomCollider.name = "BottomCollider";
rightCollider.name = "RightCollider";
leftCollider.name = "LeftCollider";
//Add the colliders
topCollider.gameObject.AddComponent<BoxCollider2D>();
bottomCollider.gameObject.AddComponent<BoxCollider2D>();
rightCollider.gameObject.AddComponent<BoxCollider2D>();
leftCollider.gameObject.AddComponent<BoxCollider2D>();
//Make them the child of whatever object this script is on, preferably on the Camera so the objects move with the camera without extra scripting
topCollider.parent = transform;
bottomCollider.parent = transform;
rightCollider.parent = transform;
leftCollider.parent = transform;
//Generate world space point information for position and scale calculations
cameraPos = Camera.main.transform.position;
screenSize.x = Vector2.Distance (Camera.main.ScreenToWorldPoint(new Vector2(0,0)),Camera.main.ScreenToWorldPoint(new Vector2(Screen.width, 0))) * 0.5f;
screenSize.y = Vector2.Distance (Camera.main.ScreenToWorldPoint(new Vector2(0,0)),Camera.main.ScreenToWorldPoint(new Vector2(0, Screen.height))) * 0.5f;
//Change our scale and positions to match the edges of the screen...
rightCollider.localScale = new Vector3(colDepth, screenSize.y * 2, colDepth);
rightCollider.position = new Vector3(cameraPos.x + screenSize.x + (rightCollider.localScale.x * 0.5f), cameraPos.y, zPosition);
leftCollider.localScale = new Vector3(colDepth, screenSize.y * 2, colDepth);
leftCollider.position = new Vector3(cameraPos.x - screenSize.x - (leftCollider.localScale.x * 0.5f), cameraPos.y, zPosition);
topCollider.localScale = new Vector3(screenSize.x * 2, colDepth, colDepth);
topCollider.position = new Vector3(cameraPos.x, cameraPos.y + screenSize.y + (topCollider.localScale.y * 0.5f), zPosition);
bottomCollider.localScale = new Vector3(screenSize.x * 2, colDepth, colDepth);
bottomCollider.position = new Vector3(cameraPos.x, cameraPos.y - screenSize.y - (bottomCollider.localScale.y * 0.5f), zPosition);
}
}
}
The ball collides with the paddles and bounces off as it should as well as collides with the surrounding box colliders and bounces off perfectly. However, the paddles move right through the surrounding box colliders (EdgeColliders). Please note that I use Touchscript package from the unity asset store to control movement. It does not move the Rigidbody it uses transform. Another thing to note is that when I make the paddles very light (0.0001 mass) and add gravity to them, they do collide with the edge colliders and don't go through the screen.
If you want the paddle to collide with the surrounding box, you should move the Rigidbody with AddForce(), not move the transform.
Or you can limit the movement of the paddles like this :
if (transform.position.x < LeftScreenEdge)
{
transform.position = new Vector3(LeftScreenEdge, transform.position.y);
}
Delete Rigidbody2D attached to paddles.
Get the mouse pos int world coordinates:
Vector2 mP = Camera.main.ScreenToWorldPoint(Input.mousePosition);
And to prevent it from going out of boundaries just clamp It's position:
Vector3 screenBounds;
void Start()
{
screenBounds = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, Screen.height, Camera.main.transform.position.z));
}
void SetPaddlePos()
{
Vector2 clampedPos;
clampedPos.x = Mathf.Clamp(mP.x, screenBounds.x, -screenBounds.x);
clampedPos.y = Mathf.Clamp(mP.y, screenBounds.y, -screenBounds.y);
paddle.transform.position = clampedPos;
}
Related
I'm really struggling with this. I have a turret that is mounted on a spaceship. It's a child object of the ship. The turret is allowed to point left, right, up, but not down, because that would be pointing through the spaceship.
So, I need to limit the rotation of the turret so it won't point down. I started with this code:
`
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TurretScriptTest : MonoBehaviour
{
public float rotateSpeedBase = 1f;
public float rotateSpeedCurr = 1f;
public float yMin = 0;
public float yMax = 1;
public Transform target;
// Start is called before the first frame update
void Start()
{
rotateSpeedCurr = rotateSpeedBase;
}
// Update is called once per frame
void Update()
{
if (target && target.gameObject.activeSelf)
{
// Rotate
RotateWithLock();
}
}
public virtual void RotateWithLock()
{
if (target)
{
Vector3 targetDir = target.position - transform.position;
float step = rotateSpeedCurr * Time.deltaTime;
Vector3 newDir = Vector3.RotateTowards(transform.forward, targetDir, step, 0.0f);
newDir.y = Mathf.Clamp(newDir.y, yMin, yMax);
transform.rotation = Quaternion.LookRotation(newDir);
}
}
}
`
This works great except for one thing. The spaceship is also rotating all over the place. As soon as the parent spaceship goes off its original axis the above code is worthless. The turret will point through the spaceship or whatever else is in the way.
I'm assuming that what I need to do is convert everything to local rotation and position so that "down" for the turret will always be its base, not "down" in world space. So I try this code:
`
public virtual void RotateWithLock()
{
if (target)
{
Vector3 targetDir = target.position - transform.position;
float step = rotateSpeedCurr * Time.deltaTime;
Vector3 newDir = Vector3.RotateTowards(transform.forward, targetDir, step, 0.0f);
Vector3 newDirLocal = transform.InverseTransformDirection(newDir);
newDirLocal.y = Mathf.Clamp(newDirLocal.y, yMin, yMax);
transform.localRotation = Quaternion.LookRotation(newDirLocal);
}
}
`
Now the turret doesn't move at all. What to do?
If your turret's gun is a child of the turret's base, then you can just use "transform.localRotation" to get the rotation relative to the base.
If not, then try this:
If you have the Transform of the base, then you can use "Quaternion.Inverse" to get the inverse of the base's rotation. Then just take the LookRotation and multiply it by this inverse (Quaternion1 * Quaternion2 just adds them together). To convert that to a Direction Vector for the clamp calculations, just do "Quaternion * new Vector3(0, 0, 1)"
Vector3 targetDir = target.position - transform.position;
//Gets rotation to the target
Quaternion rotToTarget = Quaternion.LookRotation(newDirLocal);
float step = rotateSpeedCurr * Time.deltaTime;
//Gets new rotation
Quaternion newRot = Quaternion.RotateTowards(transform.rotation, rotToTarget, step);
//Gets the local rotation by adding (yes adding) the inverse of the base rotation
//Make sure to set the turretBase variable to the turret base's GameObject
newRot = newRot * Quaternion.Inverse(turretBase.transform.rotation);
//Gets a vector that points to the direction
Vector3 dirFromRot = newRot * new Vector3(0, 0, 1);
//This sets the y-limit. The yLimit should be less than 1.
//I am using your code, but a more effective way would be to limit the angle of the quaternion.
dirFromRot.y = Mathf.Clamp(dirFromRot.y, -yLimit, yLimit);
//Gets the LookRotation of this clamped Vector3 and sets the result.
transform.localRotation = Quaternion.LookRotation(dirFromRot.normalized);
I'm new to Raycasting so I might be going about this in a bad way, but I would to send a raycast outward to direction a gameobject is facing, bounce off the first object it hits and go a short distance before disappearing.
As far as I can tell there is no built in function for reflecting raycasts in Unity, so I have been trying to generate a another raycast where the first one hits but my luck hasn't been going well. Here's what I have so far:
public Gameobject firePoint; // I have an object attached to my main object that I use as a point of origin
void DrawLazer()
{
Vector2 origin = new Vector2(firePoint.transform.position.x, firePoint.transform.position.y);
Vector2 direction = transform.TransformDirection(Vector2.up);
RaycastHit2D hit = Physics2D.Raycast(origin, direction, 10f);
Debug.DrawLine(origin, direction *10000, Color.black);
if (hit)
{
Debug.Log("Hit: " + hit.collider.name);
var whatWeHit = new Vector2(hit.transform.position.x, hit.transform.position.y);
var offset = whatWeHit + hit.point;
offset.y = 0;
RaycastHit2D hit2 = Physics2D.Raycast(offset, Vector3.Reflect(direction, hit.normal) * -10000);
if (hit2)
{
Debug.DrawLine(offset, -Vector3.Reflect(direction, hit.normal) * -10000);
}
}
}
I call DrawLaxer(); in update.
This current script is sort of able to generate a 2nd raycast, however as you can see, the first raycast still does not stop when it hits something, and more importantly while this solution works well when it hits a flat object on a horizontal plane. But if it hits on object on a vertical or diagonal plane it applys several calculations to the wrong axis:
Any help would be greatly appreciated.
Here is a complete example monobehavior that uses Vector2.Reflect and SphereCast.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ReflectionExample : MonoBehaviour
{
public GameObject firePoint;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
DrawPredictionDisplay();
}
private void DrawPredictionDisplay()
{
Vector2 origin = firePoint.transform.position; //unity has a built in type converter that converts vector3 to vector2 by dropping the z component
Vector2 direction = firePoint.transform.up;
float radius = 1.0f;
RaycastHit2D distanceCheck = Physics2D.Raycast(origin, direction);
RaycastHit2D hit = Physics2D.CircleCast(origin, radius, direction);
Debug.DrawLine(origin, direction * 10000, UnityEngine.Color.black);
DrawCircle(origin, 1.0f, UnityEngine.Color.black);
if (hit)
{
origin = hit.point + (hit.normal * radius);
direction = Vector2.Reflect(direction, hit.normal);
hit = Physics2D.CircleCast(origin, radius, direction);
Debug.DrawLine(origin, direction * 10000, UnityEngine.Color.blue);
DrawCircle(origin, 1.0f, UnityEngine.Color.blue);
}
}
private void DrawCircle(Vector2 center, float radius, UnityEngine.Color color)
{
Vector2 prevPoint = new Vector2(Mathf.Sin(0f), Mathf.Cos(0f));
for (float t = 0.1f; t < 2 * Mathf.PI; t = t + 0.1f)
{
var nextPoint = new Vector2(Mathf.Sin(t), Mathf.Cos(t));
Debug.DrawLine(center + prevPoint, center + nextPoint, color);
prevPoint = nextPoint;
}
}
}
I've got two objects in World Space.
One is a cube has no parent.
The second is a triangle and it has a parent.
I change the cube's position and rotation.
And now I need to put cube at its first position but move triangle in the parent (local) in such position to fit the same position as if the cube won't be placed at the previous position.
Somwhere store the original position and rotation of cube
Vector3 origPosition = cube.transform.position;
Quaternion origRotation = cube.transform.rotation;
Get the offset values between cube and triangle
Vector3 posOffset = triangle.transform.position - cube.transform.position;
Quaternion rotOffset = Quaternion.Inverse(cube.transform.rotation) * triangle.transform.rotation;
(Re)Set cube and Triangle into place
cube.transform.position = origPosition;
cube.transform.rotation = origRotation;
triangle.transform.position = origPosition + posOffset;
triangle.transform.rotation = origRotation * rotOffset;
Example
public class CubeMover : MonoBehaviour
{
public Transform cube;
public Transform triangle;
private Vector3 origPosition;
private Quaternion origRotation;
// Start is called before the first frame update
private void Start()
{
origPosition = cube.transform.position;
origRotation = cube.transform.rotation;
}
[ContextMenu("Test")]
public void ResetCube()
{
Vector3 posOffset = triangle.transform.position - cube.transform.position;
Quaternion rotOffset = Quaternion.Inverse(cube.transform.rotation) * triangle.transform.rotation;
cube.transform.position = origPosition;
cube.transform.rotation = origRotation;
triangle.transform.position = origPosition + posOffset;
triangle.transform.rotation = origRotation * rotOffset;
}
}
(Had no Triangle so I used the Cylinder ... I hope that's fine for you ^^)
I have two GameObjects with a box collider. One is the player, moved with a fixed velocity towards the other. The collision stops the player (the Poo character ;D)
But they overlap, you can see it here:
I don't know why this is happening. Colliding with the top or bottom works just fine...The same effect happens from the left side. The green block has just a collider, no RigidBody.
Gif:
Another Gif, with MovePosition() ... Colliding fromt the top works, but "reentering" stops the character. Why?!:
GIF, moving up and down is okay, left and right at the top of the blocks slows him down. Weird...
Movement Script:
public class PlayerController : MonoBehaviour
{
public float Speed = 10f;
private Rigidbody2D rb2D;
private Vector2 DirectionLeft;
private Vector2 DirectionRight;
private Vector2 DirectionUp;
private Vector2 DirectionDown;
private Vector2 CurrentDirection;
// Use this for initialization
void Start()
{
rb2D = GetComponent<Rigidbody2D>();
DirectionLeft = new Vector2(Speed*-1, 0);
DirectionRight = new Vector2(Speed, 0);
DirectionUp = new Vector2(0, Speed * -1);
DirectionDown = new Vector2(0, Speed);
CurrentDirection = DirectionLeft;
}
void SetAnimationDirection()
{
Vector3 scale = transform.localScale;
if (CurrentDirection == DirectionLeft)
scale.x = 1;
else
scale.x = -1;
transform.localScale = scale;
}
void FixedUpdate()
{
float moveHorizontal = Input.GetAxis("Horizontal");
float moveVertical = Input.GetAxis("Vertical");
if (moveHorizontal > 0)
CurrentDirection = DirectionRight;
if (moveHorizontal < 0)
CurrentDirection = DirectionLeft;
if (moveVertical < 0)
CurrentDirection = DirectionUp;
if (moveVertical > 0)
CurrentDirection = DirectionDown;
Debug.Log(CurrentDirection);
SetAnimationDirection();
rb2D.velocity = CurrentDirection;
}
The solution to the first problem (overlapping):
I used transform.localScale = scale to change the direction of the sprite on every FixedUpdate() and also forgot to update the factor of the scale, since I added a new extension, which resized my object by 1 more Unity unit.
The other problem (getting stuck at the edge) is solved adding setting the Edge Radius of the Collider 2D to something below 1.0f (e.g 0.09f) and also resize the bounding box. This will prevent the object from getting stuck at the edges, because the bounding box edges are now rounded.
I see that the bullets are being fired at random positions and not actually in forward direction of the camera. What's wrong here and how should I fix it?
So I am using pooling and each time the bullet is enabled this code is run:
private void OnEnable()
{
transform.position = Camera.main.transform.position;
transform.rotation =Quaternion.identity;
GetComponent<Rigidbody>().AddForce((Camera.main.transform.forward + new Vector3(0, 0, 0)) * 5000);
Invoke("Destroy", 1.5f);
}
I have also changed it to the below code but even the second one doesn't work.
private void OnEnable()
{
Rigidbody rb = GetComponent<Rigidbody>();
rb.position = Camera.main.transform.position;
rb.rotation = Quaternion.identity;
rb.AddForce((Camera.main.transform.forward + new Vector3(0, 0, 0)) * 5000);
Invoke("Destroy", 1.5f);
}
First of all make sure the code works with out pooling. Secondly disable the collider component on the bullet (they might be colliding with themselves).
I've quickly tried this on my machine and this is the result I get.
using UnityEngine;
public class BulletController : MonoBehaviour
{
[SerializeField]
GameObject bulletPrefab;
void Update()
{
GameObject bullet = Object.Instantiate(bulletPrefab);
Rigidbody body = bullet.GetComponent<Rigidbody>();
body.position = Camera.main.transform.position;
body.AddForce(Camera.main.transform.forward * 75f, ForceMode.Impulse);
}
}
Here is the code I use with direction, position and velocity variables.
void Inception::shootGeode(std::string typeGeode, const btVector3& direction) {
logStderr(VERBOSE, "MESSAGE: Shooting geode(s)...\n");
std::shared_ptr<Geode> geode;
glm::vec3 cameraPosition = m_camera->getPosition();
btVector3 position = glm2bullet(cameraPosition + 3.0f * glm::normalize(m_camera->getTarget() - cameraPosition));
if (typeGeode == "cube")
geode = m_objectFactory->createCube("cube", new btBoxShape(btVector3(1.0f, 1.0f, 1.0f)), position, "dice");
if (typeGeode == "sphere")
geode = m_objectFactory->createSphere("sphere", new btBoxShape(btVector3(1.0f, 1.0f, 1.0f)), position);
btVector3 velocity = direction;
velocity.normalize();
velocity *= 25.0f;
geode->getRigidBody()->setLinearVelocity(velocity);
}