Attempting to understand 2D tutorial; Unity C# questions about projectile script

Hello.

I am starting to learn Unity after dabbling a bit in game programming from scratch (Visual Studio + a game library; I made a basic version of Pong). For some background, I worked through a Python workbook and took a college course in introductory C++ as well. I know my way around a more basic program, and understand many programming fundamentals, but I’m having a bit of trouble understanding some of the scripts that I’m coming across in Unity tutorials. I think that it is the shift from programming from scratch to using an engine that handles so much of the work that is often throwing me off. I went through the Unity Script Reference and the related videos and did some searches but I still have questions.

I am working with this tutorial, and I am currently stuck on the C# script below, copied from the tutorial on this page (not my formatting or comments). The script creates a projectile in the center of the player object (a different script causes it to move, I believe). Just to be clear, the script works fine, but I’m just having trouble understanding how some of it works.

using UnityEngine;

/// <summary>
/// Launch projectile
/// </summary>
public class WeaponScript : MonoBehaviour
{
  //--------------------------------
  // 1 - Designer variables
  //--------------------------------

  /// <summary>
  /// Projectile prefab for shooting
  /// </summary>
  public Transform shotPrefab;

  /// <summary>
  /// Cooldown in seconds between two shots
  /// </summary>
  public float shootingRate = 0.25f;

  //--------------------------------
  // 2 - Cooldown
  //--------------------------------

  private float shootCooldown;

  void Start()
  {
    shootCooldown = 0f;
  }

  void Update()
  {
    if (shootCooldown > 0)
    {
      shootCooldown -= Time.deltaTime;
    }
  }

  //--------------------------------
  // 3 - Shooting from another script
  //--------------------------------

  /// <summary>
  /// Create a new projectile if possible
  /// </summary>
  public void Attack(bool isEnemy)
  {
    if (CanAttack)
    {
      shootCooldown = shootingRate;

      // Create a new shot
      var shotTransform = Instantiate(shotPrefab) as Transform;

      // Assign position
      shotTransform.position = transform.position;

      // The is enemy property
      ShotScript shot = shotTransform.gameObject.GetComponent<ShotScript>();
      if (shot != null)
      {
        shot.isEnemyShot = isEnemy;
      }

      // Make the weapon shot always towards it
      MoveScript move = shotTransform.gameObject.GetComponent<MoveScript>();
      if (move != null)
      {
        move.direction = this.transform.right; // towards in 2D space is the right of the sprite
      }
    }
  }

  /// <summary>
  /// Is the weapon ready to create a new projectile?
  /// </summary>
  public bool CanAttack
  {
    get
    {
      return shootCooldown <= 0f;
    }
  }
}

I have been trying to figure out all of the details of this script on and off for the past few days, but still have a few questions:

  1. When exactly should I be using the Start() function and why? In the Unity video on this function, the person states to “use the function for anything that needs to occur when script component is enabled, which allows the programmer to delay any part of initialization code until it is really needed.” When and why would such a delay be desirable? Can’t I just initialize without this function and use the the variables when I need to? I noticed that setting shootCooldown to 0f just when it is defined and deleting the Start() function altogether seems to produce the same effect.

  2. Why is the bool variable canAttack being declared after it is first used? Is it done for a reason or is it likely a mistake or bad practice?

  3. shotTransform is declared as a var. What might be the reason for declaring this variable as a general data type? Can it be declared as anything else?

  4. Even though the projectile is created at the center of the player object (the object being controlled by the player, which is the ship), I do not see how this position is declared in the code. The code snippet shotTransform.position = transform.position; seems to be assigning some position to the projectile, but I do not understand how it is the center that is being assigned. Is it the center by default and no x,y position is assigned? What is this code snippet doing?

  5. How does Time.deltaTime work in this script (I’m having trouble understanding how it generally works and need more examples)? What is being counted down and from what number?

I would be grateful for any insights.

Start is similar to a Constructor (albeit you can’t overload it) and can be used for any initialization.

for example when it relies on other stuff beeing in a certain state. when you have a monster which strength depends on the players level you spawn/instantiate it inactive (disabled) and when the player approaches you query its level and set monsters strength health etc. but i admit i don’t use the delay effec often and if you want to pass some parameters you need a custom initialize method anyway.

this property is a class member and thus it plays no role in which order you declare it as the code is called in any order anyway. usually you group private fields, public fields, properties and private and public methods together. and within a group you order to your liking (fe alpahetically). only in an executable block (ie a method) local variables must be declared before use.

some people tend to avoid var for obvious types as it can cost little performance and makes the code less readable. in this case you could(should?) also use Transform. but thats personal preference.

transform is a property available in all scripts refering to the transform of the gameobject the script is attached to. similar ones are renderer, rigidbody and gameObject. Look here for what is available to access built in components. be aware that all but transform and gameobject may be missing so check if they exist before accessing them.
so what this line effectively does is setting the position of the newly created shot to the position of the player/ship where this script is attached to.

it simply retrieves the time in seconds since the last frame. you use it to move things framerate independant. if your games runs 30 frames on one computer and 60 frames on the other an object will travel twice as fast at the latter if you add a constant speed in update. by multiplying with time.deltatime this does not happen any more and the speed is equally fast no matter how fast/slow your game runs.

Thank you for the detailed reply.

It then seems like Start() is simply a convenient way of initializing variables right before they are necessary (since the method/function is predefined and automatically provided for you when creating a script), even though the same effect may be reached with other methods. Would this be correct?

I tried Transform in place of var and the script still works. I like the way it reads now. But why would var create a larger performance hit than other data types? Is it because the compiler has to go through the additional step of analyzing the variable and assigning its true data type? (Come to think of it, the use of var is much like Python programming in general, as the data types are not declared before they are used as far as I remember.)

I have another question about the script. The statement move.direction = this.transform.right uses the keyword this, but I do not fully understand the context. This transformation is being applied to the projectile, not to the player object, but what overarching class is allowing this to refer to the projectile instead of the player object? Or is this relative to move.direction, which refers to the projectile, and thus this.transform.right refers to the projectile as well? (I hope that the question is clear.)

start and awake are used for initialization. when that happens usually does not play an important role. usually its even better to do all initialization at scene start to avoid framerate drops. so don’t worry too much about when it happens. just be aware of it.

i read different opinions about implicit typing. as the type is determined at compile time it should theoretically not have an performance issue. but i’m not sure and i also don’t care as i try to avoid it because of readability. its probably a shortcut for the “lazy” developer and has little practical use. so also don’t worry about this.

this is the instance of the class/script and is added implicetly by the compiler so you can also omit it. here is a possible use (not good style anyway):

public class Test
{
	private int x;
	public Test(int x)
	{
		this.x = x;	// this.x is the class field, x is the constructor parameter, naming should be distinct though
	}
}

Thanks for the reply.

Regarding this – I understand your example and the general usage of this keyword. But are you saying that, in this case, the code snippet would work in the same way without this? I figured that it was bad practice to use here, as the code is all within a single class, and looks like it serves no function. (I would test it myself, but I’m not at the point where I’m attaching this script to enemies and testing out its function, as I’m trying to understand everything line-by-line. Also, I’m not confident that I understand enough yet to fully understand the results.)

Coming back to Time.deltaTime as it is used within Update() – I see that it serves as a countdown, counting back down to 0 or less, to then be rechecked within Attack through CanAttack(). If 0.25f refers to 1/4 of a second, then does that mean every iteration of Update() is 0.01 of a second, every time? Meaning that it is checked every 1/100 of a second? I thought that it was not a fixed number and varies with the complexity of the rest of the computations.

yes. as i mentioned it is added implicitely by the compiler. so you only have to use it explicitely to resolve such “conflicts” as in my example.

no. the update frequency depends on your framerate and this depends on how many things you do and how much you render in your game. you can have 700 fps or 5 fps and the delta time (time since last frame) will always be different. also not all frames have the same length so when you load something huge in one frame or the garbage collector kicks in it will take longer than the previous one. so delta time is quite different and thus this value provides you a measure.
so when you use it as a countdown you have the initial time/frequency with which things happen and then you subtract the time each frame takes and when there is no more time left it can happen again.

So this means that the 0.25 second cooldown will always be such in game time, but not in real-time. In other words, if the framerate decreases, the passage of 0.25 seconds in game time is not affected, but will certainly look like it is slower to the player playing the game in real-time. Is this correct?

no. if you don’t touch Time.timescale game time should be equal to real time no matter what the framerate is. if you set another time scale the time passes faster or slower (or is even stopped at 0.0). so the delta time (and also physics + particles etc) are affected by the timescale. but if you leave it as is game time and real time should be more or less equal.

With your help, I think that I’ve got this script and some of the others in this tutorial figured out. Once I know the basics, it gets much easier to read and understand. Thanks.