How to smooth my accelerometer-based character movement in Unity3D? - c#

I am building an infinite vertical platformer for mobile platforms and I am using the accelerometer to move the player left and right. The further the device is tilted the faster the player moves across the screen. Currently my player is a little too shakey and I would like to create a more fluid movement across the screen. Here is my code to move the character:
/********************************* Variables **************************************/
// Variables
float jumpForce = 700f;
float maxSpeed;
float acceleration;
/********************************* Start Method ************************************/
void Start ()
{
acceleration = Mathf.Abs (Input.acceleration.y);
}
/************************************ Fixed Update *************************************/
void FixedUpdate () {
if (acceleration < 0.2f) {
maxSpeed = 17;
} else if (acceleration < 0.9f) {
maxSpeed = 25;
} else {
maxSpeed = 40;
}
float move = Input.acceleration.x;
rigidbody2D.velocity = new Vector2 (move * maxSpeed, rigidbody2D.velocity.y);
/******************************* Collision Function ******************************/
void OnCollisionEnter2D(Collision2D coll)
{
foreach(ContactPoint2D contact in coll.contacts)
{
rigidbody2D.AddForce (new Vector2(0, jumpForce));
}
}

Can't you just use your Input.acceleration.y as the speed? That way you wont need the if conditions. If that's not enough, then multiply it by a multiplier.
void Update () {
maxSpeed = Mathf.Abs(Input.acceleration.y);
speedMultiplier = 30;
float move = Input.acceleration.x;
rigidbody2D.velocity = new Vector2 (move * maxSpeed * speedMultiplier, rigidbody2D.velocity.y);
You can just adjust the speedMultiplier to match your needs afterwards.
Also if you have some physics works (in this case rigidbody2D.velocity), you should use Update() instead of FixedUpdate().

You can use Mathf.Lerp method for smoothing the velocity values.
Try to use
maxSpeed = Mathf.Lerp(maxSpeed, 17, Time.deltaTime);
instead of
maxSpeed = 17;

Related

bike movement feels like the bike's on ice when turning

whenever I turn my bike it feels like it's sliding. Here's the script
[SerializeField] float turn;
[SerializeField] float maxSpeed;
[SerializeField] GameObject bikeParts;
[SerializeField] float tilt = 20f;
Rigidbody rb;
float lastY;
void Start()
{
rb = GetComponent<Rigidbody>();
}
void Update()
{
float straightMov = Input.GetAxis("Vertical");
float horizontalMov = Input.GetAxis("Horizontal");
ControlStraightMov(straightMov);
ControlWeightTurning(horizontalMov);
lastY = transform.position.y;
}
private void ControlWeightTurning(float horizontalMov)
{
Vector3 bikePartsRot = bikeParts.transform.eulerAngles;
bikePartsRot.x = tilt * horizontalMov;
bikeParts.transform.eulerAngles = bikePartsRot;
transform.Rotate(0f, (turn * Time.deltaTime) * horizontalMov, 0f);
}
private void ControlStraightMov(float straightMov)
{
if (straightMov > 0)
{
rb.AddRelativeForce((accel * Time.deltaTime) * -straightMov, 0f, 0f);
}
else if (straightMov < 0)
{
rb.AddRelativeForce(((accel / 1.3f * Time.deltaTime) * -straightMov), 0f, 0f);
}
}
Whenever the bike picks up speed, it becomes very difficult to turn the bike because the force that was added keeps moving the bike in a different direction than the direction the bike's facing, how do I apply that force it was facing before on the direction it is facing now so that it doesn't feel like sliding?
I have tried fixing this problem but i just can't because i am pretty new to unity.
how do I apply that force it was facing before on the direction it is facing now
Not sure if this would feel right but you could probably do it like e.g.
private void Update ()
{
...
rb.velocity = transform.forward * rb.velocity.magnitude;
}
Which will keep the current speed and just redirect it into the new forward direction.
In general note: Whenever there is a Rigidbody involved you don't want to set any manipulation through the Transform component but only via the Rigidbody.
So you should not do
bikeParts.transform.eulerAngles = bikePartsRot;
transform.Rotate(0f, (turn * Time.deltaTime) * horizontalMov, 0f);
but both times rather calculate the target rotation and go through e.g. rb.MoveRotation in FixedUpdate.
Which might look somewhat like
float horizontalMove;
void Update()
{
var straightMove = Input.GetAxis("Vertical");
// Get and store the input in Update
horizontalMove = Input.GetAxis("Horizontal");
ControlStraightMov(straightMove);
lastY = transform.position.y;
}
private void FixedUpdate ()
{
// Handle it in FixedUpdate
ControlWeightTurning(horizontalMove);
}
private void ControlWeightTurning(float horizontalMove)
{
var bikePartsRot = bikeParts.transform.eulerAngles;
bikePartsRot.x = tilt * horizontalMove;
// Calculate the new final rotation
var newRotation = Quaternion.Euler(bikePartsRot) * Quaternion.Euler(Vector3.up * (turn * Time.deltaTime) * horizontalMove));
// Apply it only via the Rigidbody
rb.MoveRotation(newRotation);
}

How to create a 'sprint' ability in a 2d game

I'm trying to create an ability of sprinting in 2d Unity (C#) that has an energy bar so it can't be used endlessly. What am I missing?
I've tried making sprint a function and call it when the X key was pressed. Tried to multiply the position of it but I got a blinking ability over a short distance.
\\ this is my movement script, other variables we're declared earlier in the code
void Update() {
Vector2 mi = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"));
mv = mi.normalized * speed;
}
private void FixedUpdate() {
rb.MovePosition(rb.position + mv * Time.fixedDeltaTime);
}
I expect the code to make the player go twice the normal speed when the X key is pressed but it can only be used when the energy hasn't ran out.
Don't see your implementation of "energy" but I would simply use a
public float energy;
// How much is energy reduced per second?
public float decreaseSpeed;
for increasing the speed if x is pressed do
private void Update()
{
Vector2 mi = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"));
mv = mi.normalized * speed;
if(Input.GetKey(KeyCode.X) && energy > 0)
{
// Reduce energy by decreaseSpeed per second
energy -= decreaseSpeed * Time.deltaTime;
// if needed avoid negative value
energy = Mathf.Max(0, energy);
// double the move distance
mv *= 2;
}
}
Btw ot is recommended to use Time.deltaTime instead of Time.fixedDeltaTime also in FixedUpdate (see Time.fixedDeltaTime)
In Update check if sprint axis (create a Sprint axis in the project input settings). Also, have a variable for how fast your energy bar drains while sprinting:
public float energyBarDrainRate = 1f;
private bool isSprinting = false;
void Update() {
isSprinting = Input.GetAxis("Sprint") > 0f;
Vector2 mi = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"));
mv = mi.normalized * speed;
}
Then in fixedupdate, check if your energy bar has enough to drain for that frame, and if it does then drain it and increase the effective movespeed that frame:
private void FixedUpdate() {
float energyToDrain = energyBarDrainRate * Time.deltaTime
bool drainSprint = energyAmount > energyToDrain ;
float effectiveMv = mv;
if (drainSprint && isSprinting) {
effectiveMv = mv * 2f;
energyAmount -= energyToDrain;
}
rb.MovePosition(rb.position + effectiveMv * Time.deltaTime);
}

How to move 2D Object with WASD in Unity

My code below only works for horizontal movement. Shouldn't the vertical movement be working too? I'm just starting out with basic 2D Unity programming:
public class Player : MonoBehaviour {
//These fields will be exposed to Unity so the dev can set the parameters there
[SerializeField] private float speed = 1f;
[SerializeField] private float upY;
[SerializeField] private float downY;
[SerializeField] private float leftX;
[SerializeField] private float rightX;
private Transform _transformY;
private Transform _transformX;
private Vector2 _currentPosY;
private Vector2 _currentPosX;
// Use this for initialization
void Start () {
_transformY = gameObject.GetComponent<Transform> ();
_currentPosY = _transformY.position;
_transformX = gameObject.GetComponent<Transform> ();
_currentPosX = _transformX.position;
}
// Update is called once per frame
void Update () {
_currentPosY = _transformY.position;
_currentPosX = _transformX.position;
float userInputV = Input.GetAxis ("Vertical");
float userInputH = Input.GetAxis ("Horizontal");
if (userInputV < 0)
_currentPosY -= new Vector2 (0, speed);
if (userInputV > 0)
_currentPosY += new Vector2 (0, speed);
if (userInputH < 0)
_currentPosX -= new Vector2 (speed, 0);
if (userInputH > 0)
_currentPosX += new Vector2 (speed, 0);
CheckBoundary ();
_transformY.position = _currentPosY;
_transformX.position = _currentPosX;
}
private void CheckBoundary(){
if (_currentPosY.y < upY)
_currentPosY.y = upY;
if (_currentPosY.y > downY)
_currentPosY.y = downY;
if (_currentPosX.x < leftX)
_currentPosX.x = leftX;
if (_currentPosX.x > rightX)
_currentPosX.x = rightX;
}
}
If I remove/comment out the _currentPosX and it's related codes then my Vertical movement works. But if I remove/comment out the _currentPosY and it's related codes then my Horizontal movement works.
But how come I'm having trouble getting them to work at the same time? I think I'm just missing something but I can't figure it out since I'm just a beginner at this.
Thanks to whoever can give advise.
EDIT: for further clarification...
I'm coding a simple 2d game that will have the player move in 4-directions using the WASD keys.
W = move up
A = move left
S = move down
D = move right
My main problem is that I can get two of the keys working only in one axis: either A and D is working for Horizontal while W and S are not working at all for Vertical movement or vice-versa.
You don't need those if statements. Just use += to append the input to the current transform position.
Move without Rigidbody:
public float speed = 100;
public Transform obj;
public void Update()
{
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
Vector3 tempVect = new Vector3(h, v, 0);
tempVect = tempVect.normalized * speed * Time.deltaTime;
obj.transform.position += tempVect;
}
Move Object with Rigidbody2D:
public float speed = 100;
public Rigidbody2D rb;
public void Update()
{
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
Vector3 tempVect = new Vector3(h, v, 0);
tempVect = tempVect.normalized * speed * Time.deltaTime;
rb.MovePosition(rb.transform.position + tempVect);
}
I suggest using the second code and moving the Rigidbody if you want to be able to detect collison later on.
Note:
You must assign the object to move to the obj slot in the Editor. If using the second code, assign the object with the Rigidbody2D to the rb slot in the Editor.
THIS CODE WORK 100% (you must try it.)
public float moveSpeed = 5;
void Start()
{
}
void Update()
{
if (Input.GetKey(KeyCode.D))
{
transform.position += Vector3.right * moveSpeed * Time.deltaTime;
}
else if (Input.GetKey(KeyCode.A))
{
transform.position += Vector3.right * -moveSpeed * Time.deltaTime;
}
else if (Input.GetKey(KeyCode.W))
{
transform.position += Vector3.up * moveSpeed * Time.deltaTime;
}
else if (Input.GetKey(KeyCode.S))
{
transform.position += Vector3.up * -moveSpeed * Time.deltaTime;
}
}
Try changing the value 0 in the Vector2 functions to current x/ y position...I ran into a similar problem with my project
if (userInputV < 0)
_currentPosY -= new Vector2 (/*current position*/, speed);

Slow down Rigidbody

I am adding forward force based on GetAxis ("Vertical") and a speed modifier up to normal max speed.
I am also allowing "speed boost" that adds more force on top of the normal added force up to boosted max speed.
what i am having hard time with is slowing the object down once boost period is over
So basically if you were booster to a faster speed then normal max speed and you cannot speed boost no more, slow the object back to normal max speed.
this is what i got so far:
float velocity;
float magnitude = myRigidBody.velocity.magnitude;
float vertical = Input.GetAxis ("Vertical");
if (Input.GetKeyUp(KeyCode.LeftShift)) {
boost = false;
}
if (Input.GetKeyDown (KeyCode.LeftShift)) {
boost = true;
}
if (!boost) {
velocity = vertical * speed;
Vector3 force = myRigidBody.transform.forward * velocity;
if (magnitude + force.sqrMagnitude < normalMaxSpeed) {
myRigidBody.drag = 0f;
myRigidBody.AddForce (force, ForceMode.Impulse);
} else if (magnitude + force.sqrMagnitude >= maxTrust + 50) {
myRigidBody.drag = 1.5f;
}
} else if ( magnitude < boostedMaxSpeed) {
velocity = vertical * (speed + speedBoost);
myRigidBody.AddForce (myRigidBody.transform.forward * velocity, ForceMode.Impulse);
This some what working but there must be a better solution other then changing drag.
Other than changing the drag, there are still other ways to do this. You just have to experiment to see which one works best.
1.Change the drag
myRigidBody.drag = 20f;
2.Add force to the opposite velocity.
public Rigidbody myRigidBody;
void FixedUpdate()
{
Vector3 oppositeVelocity = -myRigidBody.velocity;
myRigidBody.AddRelativeForce(oppositeVelocity);
}
3.Decrement or Lerp the current velocity back to the normal force.
public Rigidbody myRigidBody;
Vector3 normalForce;
void Start()
{
normalForce = new Vector3(50, 0, 0);
//Add force
myRigidBody.velocity = new Vector3(500f, 0f, 0f);
//OR
//myRigidBody.AddForce(new Vector3(500f, 0f, 0f));
}
void FixedUpdate()
{
//Go back to normal force within 2 seconds
slowDown(myRigidBody, normalForce, 2);
Debug.Log(myRigidBody.velocity);
}
bool isMoving = false;
void slowDown(Rigidbody rgBody, Vector3 normalForce, float duration)
{
if (!isMoving)
{
isMoving = true;
StartCoroutine(_slowDown(rgBody, normalForce, duration));
}
}
IEnumerator _slowDown(Rigidbody rgBody, Vector3 normalForce, float duration)
{
float counter = 0;
//Get the current position of the object to be moved
Vector3 currentForce = rgBody.velocity;
while (counter < duration)
{
counter += Time.deltaTime;
rgBody.velocity = Vector3.Lerp(currentForce, normalForce, counter / duration);
yield return null;
}
isMoving = false;
}
4.Change the dynamic friction of the Physics Material. You can't use this method because it requires collider which you don't have.
//Get the PhysicMaterial then change its dynamicFriction
PhysicMaterial pMat = myRigidBody.GetComponent<Collider>().material;
pMat.dynamicFriction = 10;
It's really up to you to experiment and decide which one works best for you. They are useful depending on what type of game you are making. For example, #4 is mostly used when rolling a ball because it uses physics material which requires frequent collision to actually work.
The method used in #3 gives you control over how long the transition should happen. If you need to control that then that's the choice.

Moving cube back to its original postion

When I use transform.Translate on my cube's x and z axis it moves according to pressing keys. But I want the cube to move back slowly to it's original position when user stops pressing keys and default axis are x=0 ,z=0.
public float move = 1f;
void Update ()
{
this.transform.Translate (Input.GetAxis ("Horizontal") / move, 0f, Input.GetAxis ("Vertical") / move);
}
So your best bet is to store the original position
private Vector3 _intialPosition;
private float _duration = 0.4f;
private float _startTime;
void Awake()
{
_initialPosition = transform.position;
}
void Start() {
_startTime = Time.time;
}
And then check if the key has been pressed, and if not, have it move back towards the initial position
void Update()
{
if(Input.GetAxis("Horizontal") != 0 || Input.GetAxis("Vertical") != 0)
{
//Logic here to move via arrows...
}
else
{
transform.position = Vector3.Lerp(transform.position, _initialPosition, (Time.time - _startTime ) / _duration);
}
}
Unity Documentation
Lerp
You could use Vector3.MoveTowards to slowly move your current transform to a target or original transform position.
private Transform original;
public float speed = 0.5f;
void Awake()
{
original = transform;
}
void Update() {
float step = speed * Time.deltaTime;
transform.position = Vector3.MoveTowards(transform.position, original.position, step);
}
Unity3d documention - Vector3.MoveTowards

Categories

Resources