Hi, I come from a 2d animation background and very beginner with Unity and coding, I’ve been studying consistently but only for a year. I’m trying to create a small prototype of a bike racing game in 2D 3/4 top down (I think it’s how it is called). I successfully found a nice and extensible method for simulating gravity for the different jumps using bezier interpolations, and now I’m trying to simulate the rotation of the bike when turning left and right. I have a very basic controller with addforce for the vertical input to accelerate and angularVelocity to rotate the game object on horizontal input with a sprite child attached to it. I have 16 sprites for every rotation angle from 0(Up), -22,5, -45,5…for right, 0, 22,5, 45… for left, etc…I tried many things but i can’t make it work properly. What I want is to enable the sprites in sequence for as long as the Horizontal input is pressed left or right, they need to keep its last position so the bike can move in that direction and also the sprites must flip when pressing the opposite direction since when turns left (left leg extended) turns right (right leg extended). I know this is out of my reach and all I can find is 8/16 directional input tutorials with V/H but nothing like simulate rotation. I attach an example of what I would like to accomplish. I appreciated anyone pointing me or explaing how this could be done. Thank you.
Basically you need the Mathf.Atan2() method to convert an arbitrary 2-dimension cartesian vector into an angle, which comes out in radians.
Multiplying by Mathf.Rad2Deg lets you turn it to degrees, and other scaling / offsetting lets you turn it to other numbers going around the circle, like 8 or 16 or 24 or whatever you like.
Enclosed is a demo project that accepts 2D joystick input, showing the raw fine-grained direction alongside the quantized amount. Change the code to be 16 instead of 8 and use that to select your sprite from a table.
The code:
using UnityEngine;
// @kurtdekker
public class QuantizeAngles : MonoBehaviour
{
[Header("How many steps around circle?")]
public int NumStepsAroundCircle = 8;
[Header("Raw - not quantized.")]
public Transform RawSpinner;
[Header("Quantized into steps.")]
public Transform QuantizedSpinner;
void Update ()
{
// get h and v however you like
float h = Input.GetAxis ("Horizontal");
float v = Input.GetAxis ("Vertical");
h = -h; // invert to match controller orientation
// only do it if beyond a certain amount
Vector2 v2 = new Vector2(h, v);
if (v2.magnitude > 0.1f)
{
// get the raw angle, in radians
float radians = Mathf.Atan2(h, v);
// up to degrees
float degrees = radians * Mathf.Rad2Deg;
// drive the raw spinner
RawSpinner.rotation = Quaternion.Euler(0, 0, degrees);
// how big is a pie wedge in degrees?
float pieWedge = 360.0f / NumStepsAroundCircle;
// make sure we're positive
degrees += 360;
// back up half a pie wedge
degrees += pieWedge / 2;
// get a quantized number
int wedgeNumber = (int)(degrees / pieWedge);
// expand by original pie to return to degrees
degrees = wedgeNumber * pieWedge;
// make sure 0 to 360, just for fun (not necessary really)
degrees = degrees % 360;
QuantizedSpinner.rotation = Quaternion.Euler(0, 0, degrees);
}
}
}
Sine/Cosine/Atan2 (sin/cos/atan2) and rotational familiarity and conventions
Hi Thanks for the quick reply! the funny thing is I came across your QuantizeAngle Package from another post a few weeks ago when I was trying to figure out how to restrict the bike to only turn in 16 directions, I need to adapt it somehow since I’m using v input for acceleration and h for turning R/L. I will take a look again and mess around with and see if I can, I accept suggestions if is not too much to ask :), this is nothing similar to what I’ve been learning so far. I will study about sin/cos/atan2() until “I can explain it to my grandmother over tea and crumpets” as you say lol. Thank you so much!
So the elements of this kind of controller, when trying to mimic a wheeled vehicle, is that H to turn left/right does not immediately do so the way it might with a spaceship (for example).
Instead, whatever turning input you command must be scaled by whatever speed you are going, as obviously a parked car doesn’t turn no matter what you do to the steering wheel.
Since you’re keeping the heading, you do NOT need Mathf.Atan2() to come up with pie wedge.
You only need to quantize the heading into pie wedges.
I took a whack at it below… drop this Vehicle2D onto your up-facing car sprite… it will show you the steps of rotation and it also produces a spriteNo you can use to set your chosen sprite.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// @kurtdekker - cheesy cheap vehicle 2d controls, including sprite directional quantizer
//
// To use, put it on your one single vehicle sprite that points UP
//
// left/right to steer
// up/down to accel / brake
//
// See console log for chosen sprite number: use it to change your sprite
//
// NOTE: for 2D I prefer to use 0 as north and go around clockwise.
// This does that, unlike the QuantizeAngles demo.
public class Vehicle2D : MonoBehaviour
{
const float MaxForwardSpeed = +8;
const float MaxBackwardSpeed = -2;
const float Acceleration = 5;
const float Coasting = 1;
float LinearSpeed;
float Heading = 30;
const float TurnPerDistance = 50;
const float MaxTurnPerSecond = 180;
int numWedges = 16; // how many sprites around the 360 clock?
void Update()
{
// gather and fix input
float steering = Input.GetAxisRaw("Horizontal");
float gasBrake = Input.GetAxisRaw("Vertical");
float accelerationDeceleration = 0;
if (Mathf.Abs(gasBrake) > 0.1f)
{
// gunning it or braking (up/down)
accelerationDeceleration = Acceleration * gasBrake;
}
else
{
// coasting
if (LinearSpeed > 0)
{
accelerationDeceleration = -Coasting;
}
if (LinearSpeed < 0)
{
accelerationDeceleration = +Coasting;
}
}
// adjust speed
LinearSpeed += accelerationDeceleration * Time.deltaTime;
// I can't drive 55
LinearSpeed = Mathf.Clamp(LinearSpeed, MaxBackwardSpeed, MaxForwardSpeed);
// how hard can we steer at this speed?
float effectiveTurnRate = steering * LinearSpeed * TurnPerDistance;
// keep it real
effectiveTurnRate = Mathf.Clamp(effectiveTurnRate, -MaxTurnPerSecond, +MaxTurnPerSecond);
// steer / turn
Heading += effectiveTurnRate * Time.deltaTime;
// Quantize heading so we can ONLY head in numWedges directions, not infinitely smooth rotation
// half wedge
float spriteHeading = Heading + 180.0f / numWedges;
// divide
int spriteNo = (int)(spriteHeading * numWedges) / 360;
// think positively
spriteNo %= numWedges;
if (spriteNo < 0) spriteNo += numWedges;
Debug.Log(spriteNo);
// which way are we actually going to face and move?
float quantizedHeading = (spriteNo * 360) / numWedges;
// compute our 2D rotation (negated so it winds clockwise)
Quaternion rotation = Quaternion.Euler(0, 0, -quantizedHeading);
// set our new heading, iF we are not using sprites
// TODO: if using sprites, delete this next line
transform.rotation = Quaternion.Euler(0, 0, -quantizedHeading);
// by convention, UP is forward
Vector2 forward = rotation * Vector3.up;
// compute velocity
Vector2 velocity = forward * LinearSpeed;
// update position
Vector2 position = transform.position;
position += velocity * Time.deltaTime;
transform.position = position;
// TODO: set your sprite according to spriteNo
// WARNING: remember this Transform is being rotated above!!!
//
// You must either:
// - delete the line setting transform.rotate above
// OR
// - put your vehicle sprite in a separate hierarchy (ugh)
//
// If you don't handle this you will get "double rotation,"
// from both your sprite graphics AND this transform's rotation!
}
}
Man I can’t thank you enough, this is far beyond I could ask, I really appreciate you taking the time. I played with it a lite this morning, and it works like a charm! I’ll have to work a little more on it to flip the sprites to mach the leg extension depending on what side it turns, but man…you are a legend and I learned a lot thanks to you. will post some progress when I have everything figured out.