So I was working through this tutorial.
https://www.youtube.com/watch?v=OBtaLCmJexk
and I can't find the error in my code anywhere
The problem is just that after working on the vertical ray collisions I run the program and the block just falls through the obstacle.
here is the player code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(Controller2D))]
public class Player : MonoBehaviour {
float gravity = -20;
Vector3 velocity;
Controller2D controller;
void Start() {
controller = GetComponent<Controller2D>();
}
void Update() {
velocity.y += gravity * Time.deltaTime;
controller.Move(velocity * Time.deltaTime);
}
}
and here is the Controller2D code:
using UnityEngine;
[RequireComponent(typeof(BoxCollider2D))]
public class Controller2D : MonoBehaviour {
public LayerMask collisionMask;
const float skinWidth = .015f;
public int horizontalRayCount = 4;
public int verticalRayCount = 4;
float horizontalRaySpacing;
float verticalRaySpacing;
BoxCollider2D collider;
RaycastOrigins raycastOrigins;
void Start() {
collider = GetComponent<BoxCollider2D>();
CalculateRaySpacing();
}
public void Move(Vector3 velocity) {
UpdateRaycastOrigins();
VerticalCollisions(ref velocity);
transform.Translate(velocity);
}
void VerticalCollisions(ref Vector3 velocity) {
float directionY = Mathf.Sign(velocity.y);
float rayLength = Mathf.Abs(velocity.y) + skinWidth;
for (int i = 0; i < verticalRayCount; i++) {
Vector2 rayOrigin = (directionY == -1) ? raycastOrigins.bottomLeft : raycastOrigins.topLeft;
rayOrigin += Vector2.right * (verticalRaySpacing * i + velocity.x);
RaycastHit2D hit = Physics2D.Raycast(rayOrigin, Vector2.up * directionY, rayLength, collisionMask);
Debug.DrawRay(raycastOrigins.bottomLeft + Vector2.right * verticalRaySpacing * i, Vector2.up * -2, Color.red);
if (hit) {
velocity.y = (hit.distance - skinWidth) * directionY;
rayLength = hit.distance;
}
}
}
void UpdateRaycastOrigins() {
Bounds bounds = collider.bounds;
bounds.Expand(skinWidth * -2);
raycastOrigins.bottomLeft = new Vector2(bounds.min.x, bounds.min.y);
raycastOrigins.bottomRight = new Vector2(bounds.max.x, bounds.min.y);
raycastOrigins.topLeft = new Vector2(bounds.min.x, bounds.max.y);
raycastOrigins.topRight = new Vector2(bounds.max.x, bounds.max.y);
}
void CalculateRaySpacing() {
Bounds bounds = collider.bounds;
bounds.Expand(skinWidth * -2);
horizontalRayCount = Mathf.Clamp(horizontalRayCount, 2, int.MaxValue);
verticalRayCount = Mathf.Clamp(verticalRayCount, 2, int.MaxValue);
horizontalRaySpacing = bounds.size.y / (horizontalRayCount - 1);
verticalRaySpacing = bounds.size.y / (verticalRayCount - 1);
}
struct RaycastOrigins {
public Vector2 topLeft, topRight;
public Vector2 bottomLeft, bottomRight;
}
}
Edit: The code is (as I understand from a half an hour tutorial.) just defining attributes to a quad(player) so that it can collide with another quad(obstacle). at the moment the code should only work vertically. so I am testing it by drawing the obstacle under the player and pressing play. the player should come to rest on the obstacle but instead, it sinks straight through it.
My screen after selecting the player block
and asking about debugging, I dont understand the language well enough to not break anything while trying to debug it.
I just got someone on discord to help and it turns out I didn't have the obstacle set with boxcollider2D
Many Thanks all who tried.
Related
we are working on a student project and we chose to do a Mario Galaxy style platformer with planetoids and gravity (kind of a big mistake for my first coding project but I cannot back out of it now) but I am having a hard time to get the character to face it's movement direction without absolutely spazzing out.
I have only been coding for around 2 months so please excuse me being useless at trying to figure this out.
This is the code I use for movement for the character
using System.Collections.Generic;
using UnityEngine;
public class SC_RigidbodyWalker : MonoBehaviour
{
public float speed = 5.0f;
public bool canJump = true;
public float jumpHeight = 2.0f;
public Camera playerCamera;
public float lookSpeed = 2.0f;
public float lookXLimit = 60.0f;
bool grounded = false;
Rigidbody r;
Vector2 rotation = Vector2.zero;
float maxVelocityChange = 10.0f;
void Awake()
{
r = GetComponent<Rigidbody>();
r.freezeRotation = true;
r.useGravity = false;
r.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
rotation.y = transform.eulerAngles.y;
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
}
void Update()
{
}
void FixedUpdate()
{
if (grounded)
{
// Calculate how fast we should be moving
Vector3 forwardDir = Vector3.Cross(transform.up, -playerCamera.transform.right).normalized;
Vector3 rightDir = Vector3.Cross(transform.up, playerCamera.transform.forward).normalized;
Vector3 targetVelocity = (forwardDir * Input.GetAxis("Vertical") + rightDir * Input.GetAxis("Horizontal")) * speed;
Vector3 velocity = transform.InverseTransformDirection(r.velocity);
velocity.y = 0;
velocity = transform.TransformDirection(velocity);
Vector3 velocityChange = transform.InverseTransformDirection(targetVelocity - velocity);
velocityChange.x = Mathf.Clamp(velocityChange.x, -maxVelocityChange, maxVelocityChange);
velocityChange.z = Mathf.Clamp(velocityChange.z, -maxVelocityChange, maxVelocityChange);
velocityChange.y = 0;
velocityChange = transform.TransformDirection(velocityChange);
r.AddForce(velocityChange, ForceMode.VelocityChange);
if (Input.GetButton("Jump") && canJump)
{
r.AddForce(transform.up * jumpHeight, ForceMode.VelocityChange);
}
}
grounded = false;
}
void OnCollisionStay()
{
grounded = true;
}
}
And here are the code for the gravity functions
using System.Collections.Generic;
using UnityEngine;
public class SC_PlanetGravity : MonoBehaviour
{
public Transform planet;
public bool alignToPlanet = true;
float gravityConstant = 9.8f;
Rigidbody r;
void Start()
{
r = GetComponent<Rigidbody>();
}
void FixedUpdate()
{
Vector3 toCenter = planet.position - transform.position;
toCenter.Normalize();
r.AddForce(toCenter * gravityConstant, ForceMode.Acceleration);
if (alignToPlanet)
{
Quaternion q = Quaternion.FromToRotation(transform.up, -toCenter);
q = q * transform.rotation;
transform.rotation = Quaternion.Slerp(transform.rotation, q, 1);
}
}
}
I can propose an alternative approach which I believe will simplify the problem, should you choose. If the root of your character is positioned at the center of the planetoid, then all movement can be handled as a rotation on root and you won't be fighting the inertia of the character or needing to orient it to the planetoid. Jumping could be handled by adding another child below the root that can slide up and down. Hope this helps!
I managed to get it working by having a new script added to the player avatar.
Here is the code, it's basically a copy paste of a part of the RigidBodyWalker script with some more added parts. It's not perfect but it works like I wanted it to.
using System.Collections.Generic;
using UnityEngine;
public class InputRotation : MonoBehaviour
{
public Camera playerCamera;
public float speed;
public float rotationSpeed;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
//float horizontalInput = Input.GetAxis("Horizontal");
//float verticalInput = Input.GetAxis("Vertical");
}
private void FixedUpdate()
{
Vector3 forwardDir = Vector3.Cross(transform.up, -playerCamera.transform.right).normalized;
Vector3 rightDir = Vector3.Cross(transform.up, playerCamera.transform.forward).normalized;
Vector3 targetVelocity = (forwardDir * Input.GetAxis("Vertical") + rightDir * Input.GetAxis("Horizontal")) * speed;
if(targetVelocity != Vector3.zero)
{
Quaternion toRotation = Quaternion.LookRotation(targetVelocity, transform.up);
transform.rotation = Quaternion.RotateTowards(transform.rotation, toRotation, rotationSpeed * Time.deltaTime);
}
}
}
I am creating a third person player movement script. The movement adds force to the rigidbody relative to the direction of the camera. I want to have a max speed limit in the forward direction (cForward), and a separate max speed limit for the horizontal/right direction (cRight). Normally I would be fine with setting the velocity directly, however this screws up gravity for the player. Is there any way to achieve this by using addforce OR is there a way to get gravity working properly when setting velocity directly? Here is what I have so far(some of my other attempts at a solution are commented out):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public Camera mainCamera;
public float horizontalWalkSpeed = 500.0f;
public float verticalWalkSpeed = 500.0f;
public float horizontalSprintSpeed = 1000.0f;
public float verticalSprintSpeed = 1000.0f;
public bool isSprinting = false;
public bool cannotMove = false;
public float accelerationSpeed = 0.2f;
private Rigidbody pRigidBody;
private Vector2 currentInputVector;
private Vector2 smoothInputVelocity;
// Start is called before the first frame update
void Start()
{
pRigidBody = GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
HandleInput();
}
private void FixedUpdate()
{
Vector3 cForward = mainCamera.transform.forward;
Vector3 cRight = mainCamera.transform.right;
cForward.y = 0.0f;
cRight.y = 0.0f;
cForward.Normalize();
cRight.Normalize();
if (!cannotMove && !isSprinting)
{
Vector3 vForce = cForward * currentInputVector.y * verticalWalkSpeed;
Vector3 hForce = cRight * currentInputVector.x * horizontalWalkSpeed;
//Vector3 gravity = Vector3.up * -9.8f;
Vector3 force = vForce + hForce;
//float verSpeed = Vector3.Dot(pRigidBody.velocity, cForward);
//float horSpeed = Vector3.Dot(pRigidBody.velocity, cRight);
//if (verSpeed >= 0 && verSpeed <= verticalWalkSpeed)
//{
// pRigidBody.AddForce(vForce, ForceMode.VelocityChange);
//}
//if(horSpeed < horizontalWalkSpeed)
//{
// pRigidBody.AddForce(hForce, ForceMode.VelocityChange);
//}
//float velocityInDirection = Vector3.Dot(pRigidBody.velocity, cForward);
//if(velocityInDirection > verticalWalkSpeed)
//{
// pRigidBody.AddForce(-vForce, ForceMode.VelocityChange);
//}
//pRigidBody.velocity = force;
pRigidBody.AddForce(force, ForceMode.VelocityChange);
}
else if (!cannotMove && isSprinting)
{
pRigidBody.velocity = cForward * currentInputVector.y * verticalSprintSpeed * Time.fixedDeltaTime + cRight * currentInputVector.x * horizontalSprintSpeed * Time.fixedDeltaTime;
}
}
private void HandleInput()
{
isSprinting = Input.GetButton("Sprint");
Vector2 input = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"));
currentInputVector = Vector2.SmoothDamp(currentInputVector, input, ref smoothInputVelocity, accelerationSpeed);
}
}
I think this thread may help --https://answers.unity.com/questions/9985/limiting-rigidbody-velocity.html.
Basically, there are two methods.
Add force in the opposite direction and increase with the extent to which the object exceeds the limit. This requires additional calculations and tests.
simply normalize the velocity value when it exceeds the limit.
I'm trying to have an enemy circle the player and shoot bullets at said player. Whenever the enemy shoots though, the bullet is slightly off from the player and continues to shoot further from the players position.
this is the enemies behaviour
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CirclingScript : MonoBehaviour, IPooledObject
{
public Transform tf;
public Transform Target;
public GameObject Ship;
public GameObject WaveMaker;
public EnemyScript enemyscript;
public string BulletName;
ObjectPools objectPooler;
private int I; // Wave number
public int BulletAmount;
public float angle;
public float Radius;
public float RotateSpeed;
private float Timer;
public float Health;
private Quaternion BulletRot;
public void OnObjectSpawn()
{
WaveMaker = GameObject.Find("EnemyWaveMaker");//gets the game object
enemyscript = WaveMaker.GetComponent<EnemyScript>();// gets the scripts for the wave makers
I = enemyscript.i;
Ship = GameObject.Find("Ship");//gets the game object
Target = Ship.GetComponent<Transform>();// gets the Transform
//https://answers.unity.com/questions/1068513/place-8-objects-around-a-target-gameobject.html thank you ^-^
angle = enemyscript.RotSpace * Mathf.PI * 2f / enemyscript.Waves[I].Amount;
RotateSpeed = enemyscript.Waves[I].RotateSpeed;
Radius = enemyscript.Waves[I].Radius;
}
void Start()
{
objectPooler = ObjectPools.Instance;
}
void OnTriggerEnter2D(Collider2D coll)
{
if (coll.gameObject.CompareTag("Bullet"))
{
Health -= coll.GetComponent<DamageScript>().Damage;
}
if (Health <= 0)
{
objectPooler.SpawnFromPool("Boom", tf.position, Quaternion.identity);
gameObject.SetActive(false);
}
}
// Update is called once per frame
void Update()
{
angle += RotateSpeed * Time.deltaTime;
Timer += Time.deltaTime;
Vector2 offset = new Vector2(Mathf.Sin(angle), Mathf.Cos(angle)) * Radius;
tf.position = (Vector2)Target.position + offset;
if (Timer >= 2f)
{
Vector3 difference = Target.position - tf.position;
float rotationZ = Mathf.Atan2(difference.y, difference.x) * Mathf.Rad2Deg;
BulletPatternsModule.ShootArc(angle, BulletAmount, BulletName, tf, rotationZ);
Timer = 0f;
}
}
}
and the code for shooting the bullets
public static void ShootArc(float ArcSize, int BulletAmount, string BulletName, Transform tf, float Offset)//All arcs are in angles, not radians
{
float angle = 0;
angle = Offset;//Offset is to the left
for (int i = 0; i < BulletAmount; i++)
{
float AngleStep = ArcSize / BulletAmount;//Gets the step size for arc
angle += AngleStep;
objectPooler.SpawnFromPool(BulletName, tf.position, Quaternion.Euler(0, 0, angle));//Shoots the bullet
}
}
The offset makes the enemies near useless
Any help would be appreciated :)
(more text in order for me to post the question. Isn't this fun?)
It will be easier to make the enemy look at the player using this function
transform.LookAt(target.position);
The first script is just making the camera to move over the terrain this script i'm not changing anything. The second script is PatrolData with that i feed the first script with data. The last script is the LookAt that should rotate the camera just a bit before moving to the next target(waypoint).
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FlyToOverTerrain : MonoBehaviour
{
public Transform target;
public float desiredHeight = 10f;
public float flightSmoothTime = 10f;
public float maxFlightspeed = 10f;
public float flightAcceleration = 1f;
public float levelingSmoothTime = 0.5f;
public float maxLevelingSpeed = 10000f;
public float levelingAcceleration = 2f;
private Vector3 flightVelocity = Vector3.zero;
private float heightVelocity = 0f;
private void LateUpdate()
{
Vector3 position = transform.position;
float currentHeight = position.y;
if ((bool)target && flightAcceleration > float.Epsilon)
{
position = Vector3.SmoothDamp(position, target.position, ref flightVelocity, flightSmoothTime / flightAcceleration, maxFlightspeed, flightAcceleration * Time.deltaTime);
}
if (levelingAcceleration > float.Epsilon)
{
float targetHeight = Terrain.activeTerrain.SampleHeight(position) + desiredHeight;
position.y = Mathf.SmoothDamp(currentHeight, targetHeight, ref heightVelocity, levelingSmoothTime / levelingAcceleration, maxLevelingSpeed, levelingAcceleration * Time.deltaTime);
}
transform.position = position;
}
}
Then the data script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class PatrolData
{
public Transform target = null;
public float minDistance = 5f;
public float lingerDuration = 5f;
public float desiredHeight = 10f;
public float flightSmoothTime = 10f;
public float maxFlightspeed = 10f;
public float flightAcceleration = 1f;
public float levelingSmoothTime = 0.5f;
public float maxLevelingSpeed = 10000f;
public float levelingAcceleration = 2f;
}
public class PatrolOverTerrain : MonoBehaviour
{
public FlyToOverTerrain flyOverTerrain;
public enum PatrolMode { Clamp, Wrap, PingPong };
public PatrolData[] patrolPoints;
public PatrolMode mode = PatrolMode.Wrap;
private int iterator = 0;
private int index = 0;
private float lingerDuration = 0f;
public Vector3 distanceFromTarget;
private void OnEnable()
{
if (patrolPoints.Length > 0)
{
lingerDuration = patrolPoints[index].lingerDuration;
}
}
private void Update()
{
int length = patrolPoints.Length;
if (!flyOverTerrain) return;
if (patrolPoints.Length < 1) return;
if (index < 0) return;
var patrol = patrolPoints[index];
if (lingerDuration <= 0)
{
iterator++;
switch (mode)
{
case PatrolMode.Clamp:
index = (iterator >= length) ? -1 : iterator;
break;
case PatrolMode.Wrap:
iterator = Modulus(iterator, length);
index = iterator;
break;
case PatrolMode.PingPong:
iterator = Modulus(iterator, length * 2);
index = length - Mathf.Abs(length - iterator);
break;
}
if (index < 0) return;
patrol = patrolPoints[index];
flyOverTerrain.target = patrol.target;
flyOverTerrain.desiredHeight = patrol.desiredHeight;
flyOverTerrain.flightSmoothTime = patrol.flightSmoothTime;
flyOverTerrain.maxFlightspeed = patrol.maxFlightspeed;
flyOverTerrain.flightAcceleration = patrol.flightAcceleration;
flyOverTerrain.levelingSmoothTime = patrol.levelingSmoothTime;
flyOverTerrain.maxLevelingSpeed = patrol.maxLevelingSpeed;
flyOverTerrain.levelingAcceleration = patrol.levelingAcceleration;
lingerDuration = patrolPoints[index].lingerDuration;
}
Vector3 targetOffset = Vector3.zero;
if ((bool)patrol.target)
{
targetOffset = transform.position - patrol.target.position;
}
float sqrDistance = patrol.minDistance * patrol.minDistance;
if (targetOffset.sqrMagnitude <= sqrDistance)
{
flyOverTerrain.target = null;
lingerDuration -= Time.deltaTime;
}
else
{
flyOverTerrain.target = patrol.target;
}
distanceFromTarget = transform.position - patrol.target.position;
}
private int Modulus(int baseNumber, int modulus)
{
return (modulus == 0) ? baseNumber : baseNumber - modulus * (int)Mathf.Floor(baseNumber / (float)modulus);
}
}
And the lookat script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LookAtCamera : MonoBehaviour {
//values that will be set in the Inspector
public Transform target;
public float RotationSpeed;
//values for internal use
private Quaternion _lookRotation;
private Vector3 _direction;
// Update is called once per frame
void Update()
{
//find the vector pointing from our position to the target
_direction = (target.position - transform.position).normalized;
//create the rotation we need to be in to look at the target
_lookRotation = Quaternion.LookRotation(_direction);
//rotate us over time according to speed until we are in the required rotation
transform.rotation = Quaternion.Slerp(transform.rotation, _lookRotation, Time.deltaTime * RotationSpeed);
}
}
All scripts are attached to the Main Camera. Until now it was working fine the camera moved between the targets. Now what i want to do is when the camera stop near each target just a bit before the camera start moving to the next target make a rotation to be facing to the target it's going to be moving to.
The problem is i don't know how to make it wait and how much and when to start the rotation in the LookAtCamera script.
Now what it does when running the game it's start rotating right away to the next target(in inspector i dragged for testing the second target).
My problem is how to work with the LookAtCamera script.
Found how to do it.
First in the LookAtCamera script that attached it to the Main Camera i also check that there is a target existing in the Update function:
if (target)
so the Update function:
void Update()
{
//find the vector pointing from our position to the target
if (target)
_direction = (target.position - transform.position).normalized;
//create the rotation we need to be in to look at the target
_lookRotation = Quaternion.LookRotation(_direction);
//rotate us over time according to speed until we are in the required rotation
transform.rotation = Quaternion.Slerp(transform.rotation, _lookRotation, Time.deltaTime * RotationSpeed);
}
Then in the script PatrolData:
The script PatrolData can be attached to any gameobject in this case i attached it to the Main Camera too.
In the top of FlyToVerTerrain i added:
public LookAtCamera lookAtCamera;
Then in the Update function:
lookAtCamera.target = patrol.target;
lookAtCamera.RotationSpeed = 3;
Then at this part:
if (targetOffset.sqrMagnitude <= sqrDistance)
{
flyOverTerrain.target = null;
lookAtCamera.target = null;
lingerDuration -= Time.deltaTime;
}
else
{
flyOverTerrain.target = patrol.target;
lookAtCamera.target = patrol.target;
}
In the end all the 3 scripts make that the camera will move to each waypoint(target), The camera will rotate and will face to the next waypoint before moving to the next waypoint.
Working perfect.
So I am trying to make something like a machine gun, and I can not make Rigidbody.AddForce() work like I want. I want instantiated bullets to fly exactly to the direction of the player, but it does not always work correctly:
And here is my code:
using UnityEngine;
using System.Collections;
public class Laser : MonoBehaviour {
public Transform target;
public GameObject ammo;
public int bulletsPerSec = 10;
public float shootPauseDuration = 3.0f;
public float force = 5.0f;
int bullets = 0;
bool pause = false;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
TrackPlayer();
}
void TrackPlayer()
{
float AngleRad = Mathf.Atan2(target.position.y - transform.position.y,
target.position.x - transform.position.x);
float AngleDeg = 180 / Mathf.PI * AngleRad;
transform.rotation = Quaternion.Euler(0, 0, AngleDeg);
Debug.Log(transform.rotation);
Shoot();
}
void Shoot()
{
if(bullets < bulletsPerSec)
{
bullets++;
GameObject bullet = Instantiate(ammo, transform.position,
transform.rotation) as GameObject;
bullet.GetComponent<Rigidbody2D>().AddForce(
transform.rotation * transform.up * force,
ForceMode2D.Impulse);
Destroy(bullet, 3.0f);
}
else if(pause==false)
{
pause = true;
StartCoroutine("ShootPause");
}
}
IEnumerator ShootPause()
{
yield return new WaitForSeconds(shootPauseDuration);
bullets = 0;
pause = false;
}
}
why did you do
// Quaternion? // * Vector3?
transform.rotation * transform.up * force
shouldn't it be...
transform.eulerAngles.normalized * force
other solution which will work
(target.position - transform.position).normalized * force