Unity- Player Jump Height / Public, Private, Static, nothing - c#

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!

Related

Why my character didn't move with this code?

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.

How to make this gameobject instantiated as a child of a parent gameobject be positioned at an offset?

I've followed a C# in Unity tutorial to create a simple Space Invaders game. I'm now trying to understand the different functions being used.
There is this class called PlayerController. It also defines a shot gameobject, a field which is then supplied with a bullet prefab:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
private Transform player;
public float speed;
public float maxBound, minBound;
public GameObject shot;
public Transform shotSpawn;
public float fireRate;
private float nextFire;
// Start is called before the first frame update
void Start()
{
player = GetComponent<Transform> ();
}
// Update is called once per frame
void FixedUpdate()
{
float h = Input.GetAxis ("Horizontal");
if (player.position.x < minBound && h < 0)
h = 0;
else if (player.position.x > maxBound && h > 0)
h = 0;
player.position += Vector3.right * h * speed;
}
void Update()
{
if (Input.GetButton("Fire1") && Time.time > nextFire)
{
nextFire = Time.time + fireRate;
Instantiate(shot, shotSpawn.position, shotSpawn.rotation);
}
}
}
The bullet gameobject used the class BulletController:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BulletController : MonoBehaviour
{
private Transform bullet;
public float speed;
// Start is called before the first frame update
void Start()
{
bullet = GetComponent<Transform>();
}
void FixedUpdate()
{
bullet.position += Vector3.up * speed;
if (bullet.position.y >= 10)
Destroy(gameObject);
}
void OnTriggerEnter2D(Collider2D other)
{
if (other.tag == "Enemy")
{
Destroy(other.gameObject);
Destroy(gameObject);
PlayerScore.playerScore++;
}
else if (other.tag == "Base")
Destroy(gameObject);
}
}
From what I understand, a Transform object has values for position, rotation and scale.
So first, what does declaring x = GetComponent; do?
Second, where does "shotSpawn" takes its values from? From the object to which the code is applied to?
Third, the bullet gets instantiated exactly at the center of the square serving as the player ship's body, but I want it start higher at the y axis so it starts at the end of the cannon shape. It also seems to graphically intersect with the ship's body, so I wanted to move it slightly into the z axis. So how can you write that? I tried adding to the value of shotSpawn.position but it keeps declaring errors.
Thanks in advance.
A GetComponent means basically just grabbing a component of an object. Take a simple example: The Main Camera has a lot of components:
1.1. A Transform component. This component is used, as you understood, to define the position, rotation and scaling of an object
1.2. A Camera component. This has plenty of fields
1.3. A Flare Layer component.
etc.
These components can be grabbed via script. The reason developers use this is for their properties. For example, by saying Transform playerTransformComp = player.GetComponent<Transform>();, you will be able to write playerTransformComp.position. position is a property of objects of type Transform.
I don't think that the GetComponent you saw in the tutorial are useful because every game object has a transform component anyway, so if they declared the player as public GameObject player;, then used player.transform.position instead, it would have been way easier. In fact, I don't think it even makes sense to declare something as Transform, and then grabbing the Transform component. As #BugFinder said in your previous post, the tutorial is pretty bad overall.
shotSpawn takes its values from... itself! It is a public object, so I'm assuming you dragged & dropped the shotSpawn object from the scene into the script's fields. This means that the shotSpawn object from the script is the object you dragged & dropped on the script's fields. You can use all of its features and the dragged & dropped object will be affected. Thus, you can use shotSpawn.position and shotSpawn.rotation. I might be repeating myself here for a little bit, but please notice that shotSpawn is a Transform object, therefore you can use a typical Transform object's properties.
The documentation on Transform.position (as well as the one on Transform.rotation say you have to use Vector3 objects to add or substract values to them.
One would do shotSpawn.position + new Vector3(10f, 5f, 10f).
Naturally, you can also do
value = new Vector3(10f, 5f, 10f);
and then Instantiate(shot, shotSpawn.position + value, shotSpawn.rotation);
Also, please (for the future), try to ask one question per post, otherwise people will ignore your question or even flag it and it will be deleted. I was once like you, so I wouldn't do that, but please take this into consideration when making further posts.

Rigidbody velocity is zero sometimes while the rigidbody is moving

I'm trying to add sound to my pushable object and just have a simple if statement for checking if the pushable object is moving. The concept is quite simple, if the object is moving the sound should play and when it's not moving it shouldn't. The problem however is, that when I debug the value there is a 0 every 5 frames or so. This causes the sound to work inconsistently. The script I have is really simple, and I have tried changing to fixedupdate, but it didn't work. I had read somewhere that physics calculations are done in fixedUpdate.
public class PushableObject : MonoBehaviour
{
Rigidbody rb;
AudioSource audioS;
bool rbIsMoving = false;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody>();
audioS = GetComponent<AudioSource>();
}
// Update is called once per frame
void FixedUpdate()
{
if (rb.velocity == Vector3.zero)
{
Debug.Log("Not moving");
audioS.volume = 0f;
}
else
{
Debug.Log("Moving");
audioS.volume = 0.1f;
}
}
}
Edit: I have just figured out that if the player pushes the pushable object into the wall the sound is still playing, for this reason I think I have to change the way too determine if the object is moving or not.
This happens due to single precision floating point.
You never (should) compare two float values directly since they might logically be equal (e.g. 1 = 5*0.2) but for the computer they might be different by a small "epsilon"
So Unity decides that Vector3 simply uses only a precision of 0.00001 for equality for ==
Rather use Mathf.Approximately which uses a very small Epsilon instead.
Than easier than comparing each component of the rb.velocity against 0 what you actually want is the rb.velocity.magnitude which is actually the overall speed.
if (Mathf.Approximately(rb.velocity.magnitude, 0))
Update
Alternatively store the last position and compare it to the current one using Vector3.Distance either again with Mathf.Approximately
private Vector3 lastPosition;
private void LateUpdate()
{
if(Mathf.Approximately(Vector3.Distance(lastPosition, transform.position), 0))
{
//...
}
else
{
//...
lastPosition = transform.positiom;
}
}
Or with a custom threshold
if(Vector3.Distance(lastPosition, transform.position) <= someThreshold)
or this time you actually can use == if a threshold of 0.00001 is what you want
if(lastPosition == transform.position)
You can check if the RigidBody is sleeping:
if (!rb.IsSleeping()
{
//it's moving
}
else
{
//it's not
}
Or check if the transform position has moved since last frame:
Vector3 lastposition;
Gameobject go = somegameobject;
function Update()
{
if (lastposition == go.transform.position)
{
//not moving
}
else
{
//moving
}
lastposition = go.transform.position;
}
You can use Transform.hasChanged to check if the player position has changed on the last update
if (!this.transform.hasChanged)
{
print("Player is not moving");
}
transform.hasChanged = false;

Unity Jump function issue

I've been working on this script for the past day. For some reason my character will not jump as long as it's animator is active. I've got into the animation (there is only one) and removed all references to the animation placing a position anywhere and still the issue presides.
I have discovered that I can make my player jump if I use Co-routine which I'm using. However, I'm still new to using them and I can't work out why my player won't fall to the ground once a force has been added to it. And my player only moves up when the button is clicked. Could someone please take a look at my script and tell me what I'm doing wrong?
public float jumpSpeed = 100.0f;
public float jumpHeight = 2.0f;
public AudioClip jumpSound;
private GameObject pos;
private bool moving;
private bool isJumping;
void Start()
{
}
// Update is called once per frame
void Update ()
{
if(Input.GetMouseButtonDown(0))// && !moving)
{
isJumping = true;
StartCoroutine(JumpPlayer(gameObject.transform.localPosition));
}
else
{
isJumping = false;
}
}
IEnumerator JumpPlayer(Vector3 startPos)
{
Vector3 jump = new Vector3(transform.localPosition.x, jumpHeight, transform.localPosition.z);
float t = 0f;
t += Time.deltaTime / jumpSpeed;
rigidbody.AddForce(Vector3.up * jumpSpeed);
//gameObject.transform.localPosition = Vector3.Lerp(startPos, jump, 0.5f);
//isJumping = false;
yield return null;
}
Firstly, your use of coroutine isn't doing anything in particular - because it only does yield return null at the end, it'll run in a single frame and then exit. You could make it a regular void function and you shouldn't see any change in behaviour.
Removing other redundant code and you have just this:
if(Input.GetMouseButtonDown(0))
{
rigidbody.AddForce(Vector3.up * jumpSpeed);
}
This force is added for only a single frame: the frame where the mouse button is pressed down (if you used Input.GetMouseButton instead, you'd see the force applied for multiple frames).
You say "my player only moves up when the button is clicked" but I'm not clear why that's a problem - perhaps you mean that the player should continue to move up for as long as the button is held, in which case you should refer to my previous paragraph.
The most obvious reasons for the player not falling again are related to the RigidBody component: do you have weight & drag set to suitable values? An easy way to test this would be to position your player some distance from the ground at the start of the scene, and ensure that they fall to the ground when you start the scene.
Another reason might be that you're using the default override of .AddForce in an Update cycle. The default behaviour of this method applies force during the FixedUpdate calls, and you might find that using ForceMode.Impulse or ForceMode.VelocityChange gives you the result you're looking for.

My character sometimes fails to jump - Unity2D

So, I've set up a basic script in Unity to move around a 2D sprite, and it works pretty well, except for the fact that occasionally the player-character will not jump when told to. It seems to only happen while or shortly after the character moves horizontally. I really have no idea why this is happening. Hopefully someone else can shed some light on this. Here is the controller script. Any feedback is helpful, even if it's unrelated to the question, I'm doing this as a learning exercise.
using UnityEngine;
using System.Collections;
public class PlayerControlsCs : MonoBehaviour {
public KeyCode walkLeft;
public KeyCode walkRight;
public KeyCode jumpUp;
public float speed = 5;
public float jumpForce = 750;
public int jumpCapacity = 1;
public int extraJumps = 0;
public bool facingRight = true;
public bool grounded = false;
private Transform groundCheck;
private Animator anim;
void Awake () {
groundCheck = transform.Find("GroundCheck");
anim = GetComponent<Animator>();
}
void Update () {
grounded = Physics2D.Linecast(transform.position, groundCheck.position, 1 << LayerMask.NameToLayer("Terrain"));
if(grounded){
anim.SetTrigger("Grounded");
anim.ResetTrigger("Falling");
extraJumps = jumpCapacity;
}
else {
anim.ResetTrigger("Grounded");
anim.SetTrigger("Falling");
}
}
void FixedUpdate () {
anim.SetFloat("Speed", Mathf.Abs(rigidbody2D.velocity.x));
anim.SetFloat("Ascent", rigidbody2D.velocity.y);
if(Input.GetKey(walkLeft))
{
if(facingRight){
Flip();
}
rigidbody2D.velocity = new Vector2(-speed, rigidbody2D.velocity.y);
}
else if(Input.GetKey(walkRight))
{
if(!facingRight){
Flip();
}
rigidbody2D.velocity = new Vector2(speed, rigidbody2D.velocity.y);
}
else
{
rigidbody2D.velocity = new Vector2(0, rigidbody2D.velocity.y);
}
if(Input.GetKeyDown(jumpUp) && grounded)
{
anim.SetTrigger("Jump");
rigidbody2D.velocity = new Vector2(rigidbody2D.velocity.x, 0);
rigidbody2D.AddForce(new Vector2(0f, jumpForce));
}
else if(Input.GetKeyDown(jumpUp) && extraJumps > 0)
{
anim.SetTrigger("Jump");
rigidbody2D.velocity = new Vector2(rigidbody2D.velocity.x, 0);
rigidbody2D.AddForce(new Vector2(0f, jumpForce));
extraJumps -= 1;
}
}
void Flip ()
{
// Switch the way the player is labelled as facing.
facingRight = !facingRight;
// Multiply the player's x local scale by -1.
Vector3 theScale = transform.localScale;
theScale.x *= -1;
transform.localScale = theScale;
}
}
If it helps at all, here is what I have made:
https://www.dropbox.com/s/ka4vgc0s0205sbd/test.html
https://www.dropbox.com/s/40i8kltwfz1jgyu/test.unity3d
Building on Max's answer...
You should use FixedUpdate() for physics stuff like applying a force to a RigidBody as it runs 50 times a second regardless of how fast the game is running. This makes it frame rate independent.
See the documentation.
Update() runs once per frame, so is frame rate dependent. In here is where most of your non-physics stuff should go, checking for inputs for example.
This video is a good explanation of the difference.
The link in the comment is also correct:
You need to call this function from the Update function, since the
state gets reset each frame
So check if is grounded only when the player presses jump as ray/linecasts are computationally expensive, apply the physics in FixedUpdate(), and check for input in Update().
Update and FixedUpdate aren't guaranteed to happen every time one after another. I haven't ran into this kind of bugs, so I can't say for sure, but you may experience a situation where your grounded state is incorrect. Instead of saving this value as a field, try checking for it every time you need it — at least a separate check in Update and FixedUpdate.
Input should be handeled in Update, because update runs every frame, while fixed update isn't like update and it doesn't run every frame so when input is handeled in fixed update it might miss the input and it won't jump !
I suggest you cut and paste all the input code from fixed update to update !

Categories

Resources