I just start making a game with Unity and Visual Studio, i have a bit confuse that why my Speed value always at zero even i set it equal to another variable.
public class Ballista : MonoBehaviour
{
public Transform firePoint;
public GameObject arrowPrefab;
public float speed = 10f;
public float startTimeHold;
public float holdDownTime;
public float arrowSpeed = 100f;
public float finalSpeed;
private void Update()
{
if (Input.GetKeyDown(KeyCode.M))
{
startTimeHold = Time.time;
}
if (Input.GetKeyUp(KeyCode.M))
{
holdDownTime = Time.time - startTimeHold;
Shoot();
finalSpeed = holdDownTime * arrowSpeed;
}
Debug.Log("holddowntimeis: " + holdDownTime);
Debug.Log("final speed is: " + finalSpeed);
}
public void Shoot()
{
GameObject arrowGO = (GameObject)Instantiate(arrowPrefab, firePoint.position, firePoint.rotation);
}
}
and this is the script that my speed value always zero
{
Rigidbody2D rgBody2D;
public GameObject ballista;
private float speed;
private void Awake()
{
rgBody2D = GetComponent<Rigidbody2D>();
}
private void Start()
{
\\ i though the code below will set my speed equal to my finalSpeed but it still 0
speed = ballista.GetComponent<Ballista>().finalSpeed;
Vector2 mousePos = Input.mousePosition;
Debug.Log("speed: " + speed);
rgBody2D.AddForce(mousePos * speed * Time.deltaTime);
}
void Update()
{
}
}
Different to what others said here you actually do NOT want to do it in Update.
Your goal here is to once give your newly spawned arrow a start velocity, not a continuous force.
The issue here is of other nature I think:
You are always spawning a new instance of your second script from a given prefab. This prefab seems to hold a reference to a Ballista prefab instance. At least you are never assigning a new value to the ballista! It might simply be a wrong reference where the finalSpeed is never updated.
Your are first doing the Shoot and after it set the finalSpeed -> even if it would be the correct reference you always get the wrong finalSpeed value!
I would actually change your two scripts in order toake your arrow instance being controlled by the Ballista instead of letting each spawned arrow poll the speed itself:
public class Ballista : MonoBehaviour
{
public Transform firePoint;
// By giving it the correct type here you don't need GetComponent later
public Rigidbody2D arrowPrefab;
public float startTimeHold;
public float arrowSpeed = 100f;
// I personally would add aax value and clamp since to fast RigidBodies can break collision detection eventually
public float maxArrowSpeed = 300;
private void Update()
{
if (Input.GetKeyDown(KeyCode.M))
{
startTimeHold = Time.time;
}
if (Input.GetKeyUp(KeyCode.M))
{
var holdDownTime = Time.time - startTimeHold;
// get the speed before you shoot
// Here personally I would clamp to be sure the arrow is never faster than maxArrowSpeed
// regardless of how long the user held the button pressed
var finalSpeed = Mathf.Min(holdDownTime * arrowSpeed, maxArrowSpeed);
Debug.Log("holddowntimeis: " + holdDownTime);
Debug.Log("final speed is: " + finalSpeed);
// pass in your speed
Shoot(finalSpeed);
}
}
private void Shoot(float speed)
{
// Instantiate anyway returns the type of the given prefab
// which now is a Rigidbody2D
var arrow = Instantiate(arrowPrefab, firePoint.position, firePoint.rotation);
// Directly set the speed from here
// -> your arrow doesn't even need an extra component
// Since you already spawned it with the correct rotation you maybe don't even need the mouse position thing
// AddForceRelative adds a force in the local space of the arrow so if the rotation is correctly
// this simply adds the force in its forward direction
// Note that also using Time.deltaTime actually only makes sense if you set something continuously
// For a one-time force you wouldn't need it, rather adjust your arrowSpeed field
arrow.AddForceRelative(Vector2.forward * speed);
}
}
Instead of using AddForce or AddForceRelative you could actually also simply set the target velocity:
arrow.velocity = Vector2.forward * speed;
Since you are not updateding it continuously this is totally fine and a lot easier to foresee the actual target speed since when adding a force you have to take the mass and frictions into account. You would than ofcourse have to adjust the arrowSpeed (and evtl maxArrowSpeed) accordingly to not anymore represent a force but an actual velocity in Units/second.
I hope I made my points clear enough, don't hesitate to ask if something stayed unclear ;)
speed = ballista.GetComponent().finalSpeed;
should Come in
void Update(){
}
block and not in
void Start() {
}
as void start is only run once and at that point in time the speed is zero
Hope it helped :)
Start happens only once.
Start is called on the frame when a script is enabled just before any
of the Update methods are called the first time.
Disregarding everything else, i am guessing you want it to update the speed every frame in Update
Update is called every frame, if the MonoBehaviour is enabled.
private void Update()
{
\\ i though the code below will set my speed equal to my finalSpeed but it still 0
speed = ballista.GetComponent<Ballista>().finalSpeed;
Vector2 mousePos = Input.mousePosition;
Debug.Log("speed: " + speed);
rgBody2D.AddForce(mousePos * speed * Time.deltaTime);
}
Related
I am moving a rigidbody using rb.AddForce(force,ForceMode.Impulse) where force is the target position the rigidbody have to reach.
Now the speed it goes directly depends on the distance it has to cover.
Let's say the time taken to reach the target position is 3sec. I need the rigidbody to cover the same target pos in 5sec.
I dont want to change the timescale as it affects my gameflow
On Changing the velocity of rigidbody it fails to reach the target position
Some basic physics/math:
velocity = change-in-position / travel-time
force = mass * change-in-velocity / acceleration-time
For ease, we're going to call change-in-position as distance, and change-in-velocity/acceleration-time as acceleration
Now, since the acceleration-time component is effectively zero because you're using Impulse, we're going to remove it from the equation (in math terms, we set it at '1')
force = mass * change-in-velocity
Assuming your object starts at zero velocity, we can simplify change-in-velocity to just velocity
force = mass * velocity
force = mass * distance / travel-time
To bring that back into Unity code:
var mass = rb.mass;
var distance = destination.position - transform.position;
var travelTime = 5f; // seconds
var force = mass * distance / travelTime;
rb.AddForce(force, ForceMode.Impulse);
Note that this assumes a frictionless transfer and constant velocity.
If you ignore gravity, this code solves the problem, here I changed the drag according to weight and distance, it may be a little bit away from the destination at the end, the reason should be higher drag friction.
public void ForceToTarget(Transform target, float time = 1f)
{
var rb = GetComponent<Rigidbody>();
var vector = target.position - transform.position;
var distance = vector.magnitude;
rb.drag = distance/time;
rb.AddForce(vector*rb.mass*distance/time, ForceMode.Impulse);
}
If you want precise control over your speed, then stop using ForceMode.Impulse because other physics effects like drag will make your answers wrong. Instead, just set the speed yourself. You can do this with a Coroutine to control timing and ForceMode.VelocityChange to control the speed. Basically, just look at where you are, where the target is, how much time is left, and apply the speed directly.
private bool canMove = true;
public void MoveTo(Vector3 targetPosition, float targetTime)
{
if(canMove)
{
StartCoroutine(MoveToCoroutine(targetPosition,targetTime));
}
}
private IEnumerator MoveToCoroutine(Vector3 targetPosition, float time)
{
canMove = false;
while(time > 0)
{
var positionDelta = transform.position - targetPosition;
var targetSpeed = positionDelta / time;
var speedDelta = targetSpeed - rb.velocity;
rb.AddForce(speedDelta , ForceMode.VelocityChange);
yield return null;
time -= Time.deltaTime;
}
// Bring the object to a stop before fully releasing the coroutine
rb.AddForce(-rb.velocity, ForceMode.VelocityChange);
canMove = true;
}
I wrote this here into the text editor, no IDE and haven't tested it, but I'm pretty sure this'll do what you want.
Assuming you're using the target position as-is then larger vectors will cause larger force to be applied than smaller vectors. Similarly, if using a direction vector as-is then as the rb gets closer to the target the magnitute of the vector gets smaller and thus less force is applied.
To get a constant speed use the direction to the target and Normalise it instead. Regardless of the distance the direction vector will always have a magnitude of 1 so you can multiply it by any value to accurately control the speed of the object:
Rigidbody rb;
public Transform target;
public float dist;
public float speed = 2f; // or whatever
public float targetDistance = 40f; // or whatever
private void Start()
{
rb = GetComponent<Rigidbody>();
StartCoroutine("IMove");
}
IEnumerator IMove()
{
dist = Vector3.Distance(transform.position, target.position);
while (dist > targetDistance)
{
dist = Vector3.Distance(transform.position, target.position);
rb.AddForce(Vector3.Normalize(target.position - transform.position) * speed, ForceMode.Impulse);
yield return new WaitForFixedUpdate();
}
}
Without getting too much into the physics and maths, if you want it to travel slower but the same distance you need to reduce the gravity on it and the initial force.
Note in this example I am assuming the weight is 1 to make the calculation a bit easier for force.
public class TrevelSpeedAdjusted
{
public float speedFactor = 1;
void FixedUpdate()
{
// Reduce the gravity on the object
rb.AddForce(-Physics.gravity * rigidbody.mass * (1 - speedFactor));
}
public float AddAdjustedForce(Vector3 force, ForceMode forceMode)
{
rb.AddForce(force * speedFactor, forceMode);
}
}
So you can try DoTween package to do this pretty easily and its very convenient to use a package instead of using Unity's inbuilt system.
With doTween use this:
DOMove(Vector3 to, float duration, bool snapping) condition to tween your physics Gameobject to a given target position in the duration you require.
Here's documentation you can refer to if you want: http://dotween.demigiant.com/documentation.php
Let me give you an example:
Install the doTween Package. http://dotween.demigiant.com/download
Import it to unity.
Go to your script where you want to achieve the functionality you mentioned on your question and add this header "using DG.Tweening".
Now get access of your RigidBody.
For Example Lets say: I have a cube gameobject with rigidbidy and this script attached.
The Cube Initial Position is at 0,0,0.
And I want it to move to 5,5,5 in 3 seconds or 5 seconds as per your questions request. And lets say I want this to happen when I click SpaceBar on keyboard.
So I would simply do.
Rigidbody rb;
void Start()
{
rb= GetComponent<Rigibody>();
}
void Update()
{
if(Input.GetButtonDown(Keycode.Space))
{
MoveCube(new Vector3(5,5,5),5);
}
}
void MoveCube(Vector3 inTargetPosition , float durationToReachTheTarget)
{
//What this line does is first take in the target position you want your physics object to reach as it first parameter, Then takes in the duration in which you want it to reach there.
rb.DoMove(inTargetPosition,durationToReachTheTarget);
}
This should help you. But remember this is only if you okay with adding an extra package. Personally this package is very good and I would recommend you this.
I sticked my character to an empty object and applied Rigidbody, script to that empty object(To change the pivot); nevertheless, the character is not moving with this code:
public class main_character : MonoBehaviour
{
public float speed;
private float max_vel = 4f;
private bool grounded = true;
private bool flip = true;
private Rigidbody2D main_vel;
private void Awake()
{
main_vel = GetComponent<Rigidbody2D>();
}
void FixedUpdate()
{
float now_vel = Mathf.Abs(main_vel.velocity.x);
if (Input.GetKey("d") && flip)
{
flip_side();
if (now_vel < max_vel)
{
main_vel.AddForce(new Vector2(speed, 0f) * Time.deltaTime);
}
}
if(Input.GetKey("a") && !flip)
{
!flip_side();
if (now_vel < max_vel)
{
main_vel.AddForce(new Vector2(-speed, 0f) * Time.deltaTime);
}
}
}
void flip_side()
{
flip = !flip;
transform.Rotate(0f, 180f, 0f);
}
}
you either need to add a speed value to the top of the class, or dynamically generate it. I would also put an exception or debug that catches when you press the button while speed is set to zero.
if speed accelerates you're going to need to build it or call it in update and give it an initial speed + speed based on delta time. I would also clamp it to max speed there instead, as it prevents large values being tracked by the game engine.
--Regarding an empty parent situation, If there is no physics components, the child should retain the motion of it's parent. That being said, you must have a rigidbody to add force to. You look like you might be using 2d so I have less experience with those rigidbodies, but I imagine it would have to not be kinematic to accept force as you are doing. After checking for that, I would maybe add some debug that posts your vector2 and speed from inside your max_vel check.
In mini game what I'm working on in Unity I want to loop movetowards. I want to move object and back to 1st position but not only 1 time. Now I have this code what give me working "move to postition and back system". How can I change this code to make loop in moving?
public class TestMovement : MonoBehaviour
{
public float speed = 3;
public Vector3 target = Vector3.zero;
private Vector3 origin;
void Start(){
origin = transform.position;
}
void Update(){
TestTransform ();
}
void TestTransform (){
transform.position = Vector3.MoveTowards (transform.position, target, speed * Time.deltaTime);
if (transform.position == target) target = origin;
}
}
TestTransform is getting called from Update, meaning it gets called once for every frame. So, you are essentially already in a loop (an endless loop as long as your game is running). Let's look at your existing code and your desired outcome:
Your existing code starts immediately moving toward the target. Upon reaching the target, it switches the target back to the origin, and starts moving back toward this new target.
Your code is changing the "target" to origin on the first pass. After that, once it has returned to the new "target" (which is now "origin"), it's just sitting there, testing that transform.position == target, and setting target = origin over and over on every frame.
If you want the object to bounce back and forth between origin and target, there are a couple of changes you need to make:
You need a third Vector3 (let's call it "currentTarget").
On start, set currentTarget = target.
In TestTransform, change where you're testing against and changing "target" to operate on "currentTarget" instead. When you change it, you'll need to consider whether currentTarget is set to target or origin, and pick the other as your next target.
End result should be "currentTarget" changes back and forth between "target" and "origin".
Side note: as someone else mentioned in comment, testing for exact equality of vectors won't always work -- you should be checking the distance between vectors and waiting for it to be less than some very small value.
this alow you to have as many positions to move as you want, just create empty GameObjects for each target and add them to list in inspector. Your transform will move in loop, of course if you want only 2 positions add 2 elements to list. (ps. i added everything in your script just to make it easy to understand, i would recomend to make another method CheckNextPosition() with the logic that manages targets)
public class TestMovement : MonoBehaviour
{
public float speed = 3;
public List<Transform> targets= new List<Transform();
public Transform target;
protected int currentTargetIndex=0;
void Start(){
origin = transform.position;
target= targets[currentTargetIndex];
}
void Update(){
TestTransform ();
}
void TestTransform (){
transform.position = Vector3.MoveTowards (transform.position, target.position, speed * Time.deltaTime);
if (transform.position == target.position)//check if you reached the target
{
if(currentTargetIndex >= targets.Count)//check if you reached the last position in your targets list
{
currentTargetIndex=0;//go to first target in your target list
}
else
{
currentTargetIndex++;// go to next target in your target list
}
target=targets[currentTargetIndex];// set the next target
}
}
}
Use Mathf.PingPong to easily do oscillation.
float distanceBetweenPosts = Vector3.Distance(origin, target);
float distanceTowardTarget = Mathf.PingPong(Time.time * speed, distanceBetweenPosts);
float percentageDistance = distanceTowardTarget / distanceBetweenPosts;
Vector3 currentOffset = (target-origin ) * percentageDistance;
Vector3 currentPosition = origin + currentOffset;
transform.position = currentPosition;
If you want to set a specific time startTime for the target starting at origin, then you can use :
float distanceTowardTarget = Mathf.PingPong((Time.time-startTime) * speed,
distanceBetweenPosts);
I have been following a tutorial on 2D Player Controller in Unity (It's 'Live Training 16 Dec 2013 - 2D Character Controllers' video).
I was able to implement everything the tutorial showed successfully with some edits to make it work in Unity 5. Afterwards, I decided to play around with it so that I can gain a better understanding. One thing I tried to do was changing the jump height when pressing the Space key. Here is the code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RobotControllerScript : MonoBehaviour {
public float maxSpeed = 10f;
bool facingRight = true;
Animator anim;
bool grounded = false;
public Transform groundCheck;
float groundRadius = 0.2f;
public LayerMask whatIsGround;
public float jumpForce = 700f;
// Use this for initialization
void Start () {
anim = GetComponent<Animator>();
}
// Update is called once per frame
void FixedUpdate () {
grounded = Physics2D.OverlapCircle(groundCheck.position, groundRadius, whatIsGround);
anim.SetBool("Ground", grounded);
//vSpeed = vertical speed
anim.SetFloat("vSpeed", GetComponent<Rigidbody2D>().velocity.y);
float move = Input.GetAxis("Horizontal");
anim.SetFloat("Speed", Mathf.Abs(move));
GetComponent<Rigidbody2D>().velocity = new Vector2(move * maxSpeed,
GetComponent<Rigidbody2D>().velocity.y);
if (move > 0 && !facingRight)
{
Flip();
}
else if (move < 0 && facingRight)
{
Flip();
}
}
void Update()
{
if(grounded && Input.GetKeyDown(KeyCode.Space))
{
anim.SetBool("Ground", false);
GetComponent<Rigidbody2D>().AddForce(new Vector2(0, jumpForce));
}
}
void Flip()
{
facingRight = !facingRight;
Vector3 theScale = transform.localScale;
theScale.x *= -1;
transform.localScale = theScale;
}
}
Looking at the code and the tutorial explanations, jumpForce is the variable that controls how high the character will jump (force applied). So, I changed 700f to a 5f. I expected the character to make a really small jump, but this was not the case. It jumped at the same height as 700f.
public float jumpForce = 700f;
After playing around with the code, I was able to have the expected result by removing the 'public' next to jumpForce. Other ways to fix this was setting it to private or static. I remember I had a similar problem when making a temperature widget on QT Creator. I had to set a variable to static or else it would not return to the default value after C to F conversion, but I don't remember exactly why.
Can anyone explain why 'public' does not work and why a private/static/nothing might? What is the best/efficient solution for this problem? Thank you very much.
When a field is public (or serializable), it is shown in inspector. When it is shown in inspector, you tweak it's value, a you expect the tweaks you make to the value, in the inspector, to be preserved between play mode runs.
When you declare a variable in the code, you may specify an initial value, like this:
public float jumpForce = 700f;
So, the first time this object gets inspected, after created, it has a reasonable value. Then, you tweak and choose a better value (or back to the same, whatever). That's the value you preferred! You want the object to keep functioning like this while you go ahead and finish making your game.
So, once a field is tweaked in Inspector, it doesn't respect the initial value you hardcoded anymore, and that's usually what everyone want. Imagine how annoying would be if everytime you make a change in your class, all the tweaked fields got back to the initial hardcoded value...
If a field was previously public, and then you make it private (or hidden in inspector by any mean), then the hardcoded initial value will stand.
But, what if I really want my object to get back to it's initial/default configuration? That's why the Reset method and the reset button in inspector were made!
I have the following code
public Rigidbody2D rb;
public float speed = 5f;
void FixedUpdate()
{
if (Input.GetKeyDown("w"))
{
Fire();
}
}
void Fire()
{
rb.MovePosition(rb.position + Vector2.down * speed * Time.deltaTime);
}
but every time I play the game, the ball does not move according to the fire function, it only goes down by 1 on the y-axis.
How can I edit my code, so that the Rigidbody moves according to the Fire() function?
There could be a couple things going wrong here. The first thing that stands out to me is that you're using Input.GetKeyDown instead of Input.GetKey-- GetKeyDown only returns true on the first frame that the key is pressed (so it's useful for things like shooting once per keypress), whereas GetKey returns true for the entire time that the key is down (so it's useful for things like movement). Changing that should give you the behavior that you're expecting.
Another issue is that you're doing input checking in FixedUpdate. It runs on a separate game loop from Update, and you can find more details about that here: Is it really wrong to use Input.GetKey() on FixedUpdate?
I would use Input and GetKey to store the input state in a Vector2 in the Update loop, and then use that data in FixedUpdate to do the actual movement:
public Rigidbody2D rb;
public float speed = 5f;
private Vector2 velocity = Vector2.zero;
void Update()
{
if (Input.GetKey("w"))
{
velocity = Vector2.down * speed;
}
else {
velocity = Vector2.zero;
}
}
void FixedUpdate()
{
MoveBall();
}
void MoveBall()
{
rb.MovePosition(rb.position * velocity * Time.deltaTime);
}
This way, you also have a stronger separation between the input code and movement code, and it will be easier to handle once you start trying to implement more complexity. Good luck!