Catalogue

Monday, September 12, 2016

Scripting 6: Script: 3D_Movement

I've been working on a 3D game recently for Itch.io's Lewd Jam. So here's the Movement script I made, keep in mind that it's my first time scripting for 3D movement. As usual, this is a C# script written in Unity.

There are 25 total variables, 20 of which are public variables. Movement scripts are central, so this amount is pretty normal as a lot of other scripts have to reference this one. 



Movement

    void Start(): Only used to set a few variables


  • LookDir: References the CamReffer object's transform component. CamReffer is an object that mirrors the camera's rotation, but ignores the x rotation.
  • SuCont: References the player object's CharacterController component. This was my first time using this component instead of Rigidbody. I hear there's a lot of benefits to it, but there are some limits that will keep me from using it more in the future, such as being unable to rotate the object's colliders.
  • animRef: References modelRef's Animator_Controller component.
    • modelRef is a public variable that has to be set from the Inspector. It references the object's model, which is parented to this object. As it can have multiple names, I have yet to figure out a way to automate this.
  • camRef: References the camera's parent object  "Camera_Control."
    • The camera in this game is parented to the Camera_Control object, which sticks right to the player. 

    void FixedUpdate(): Only being used for collision checking.
groundCheckPhysics.Raycast(transform.position,Vector3.down,0.05f,1<<0);

    void Update(): Handles the meat of the functions.

  • if(para>0)para+=-1 Time.deltaTime * Master_Script.gameSpeedTimer; else para=0;  
    • Counts down "para." I usually include countdowns in FixedUpdate for accuracy, but I need variables in this game to be manipulated by MasterScript's gameSpeedTimer variable, which is used to control time.
    • Master_Script.gameSpeedTimer controls the speed of the game. Each variable multiplied by this is affected by the time control feature.
  • setLookDir(); is called early on to easily reference the Camera_Control's rotation.
  • movementSpeed.y = 0; so that the movementSpeed Vector3 doesn't control your vertical velocity.
  • if((lookDir.rotation * movementSpeed).sqrMagnitude>maxspeed*10)movementSpeed*=0.95f;
    • This is the speed limiter. If the magnitude of your horizontal movement is greater than the allowed maxspeed, it gets pulled back down by simple multiplication.
//While in the Air
if(!groundCheck)
  • animRef.SetBool("OnGround"false); Tells the animator that you're in the air.
  • if  (!antigrav) jumpSpeed =  Mathf.Lerp(jumpSpeed,gravityset,0.01f * Master_Script.gameSpeedTimer); 
    • This part controls your falling speed. As long as the antigrav variable is turned off, you fall. Some in-game abilities stop your falling, in which case this line is ignored and you will freeze in midair.
  • accelspd = acceleration/50f; accelspd is the active acceleration used for horizontal movement, while acceleration is the character's base acceleration. These two variables are separated for flexibility, such as with this line which is meant to lower your acceleration in midair.

else (groundCheck finds ground underneath you)

  • if(jumpSpeed<0) animRef is told that you've landed on the ground through the "LandTrigger."
  • animRef is also told that you are on the ground constantly through the "OnGround" bool.
  • jumpSpeed is now set to 0 if you're still falling, this ensures the landing trigger only happens once.
  • if(actionMov) toggles wether or not you're holding the shift key.
    • If on, the animator is told and the acceleration and maxspeed are lowered.
    • If off, the speed variables accelspd and maxspeed are set higher.
  • Finally, doubleJumps are reset while you're on the ground.
//Animation variables.
  • It is at this point that the animator is given the movementSpeed and jumpSpeed floats. Horizontal movement is multiplied by the gameSpeedTimer, vertical movement isn't.
  • if(movementSpeed.magnitude ==  0 && jumpSpeed ==0) if you're not moving at all.
  • idleChecks = Random.Range(0, 800); This variable chooses a random number.
    • Now the animator is told what number idleChecks chooses. The animator knows to play a random idle animation based on which number pops up.

//Final Speed Settings.

  • tYMove = Vector3.up * jumpSpeed * 10; this is the final vertical movement.
  • finalSpeed = (movementSpeed * Time.deltaTime) + tyMove/20); 
    • This is the final movement calculation. Horizontal and vertical movements are separated so they don't affect each other.
  • SuCont.Move(finalSpeed * Master_Script.gameSpeedTimer); 
    • This is the final movement, which fed to the object's Character_Controller. finalSpeed is multipled by the gameSpeedTimer so that everything can be controlled by the time controlling function.

//Final Rotations.

  • If you're moving, lookRotation = Quaternion.LookRotation(movementSpeed, lookDir.up); creates a new rotation which looks towards whichever direction you're running in.
  • Then, as long as the character isn't stunned for any reason, the model is rotated with:
    • Quaternion.Lerp(model's rotation, lookRotation, 0.2f * the gameSpeedTimer);

    Public Void mover(int Direct0) function adds speed depending on what button is held.

  • setLookDir(); is called first to ensure the rotation is as accurate as possible.
  • Switch (Direct). Depending on the Direct variable's value, movement is added.
    • If Direct = anywhere between 1 and 4, movementSpeed += lookDir.(right or forward) * (-)accelspd; adds speed to whatever direction is pressed.

    public IEnumerator jumper( int powerdivider = 100, float timeHalt0)

  • The animator is told immediately that the "JumpTrigger" should become active.
  • jumpSpeed = 0; to stop vertical momentum.
  • antigrav = true; to halt vertical descent.
  • yield return new WaitForSeconds(timeHalt * Master_Script.gameSpeedTimer); freezes the coroutine for however long it should, most of the time is ignored as timeHalt is set to 0 by default. This was originally added in for wing flapping, but is right now being considered for removal.
  • antigravfalse; can now fall.
  • jumpSpeed = ((jumpower * 10f)/powerdivider)/10f * ((Master_Script.gameSpeedTimer/15)+1);
    • Sets the jumping movement. It's a little convoluted, but anything out of the ordinary was put there to regulate for either the time controlling variable or varied jump heights.
  • sideWaysRot = Vector3.zero; resets the rotation variable.
Void setLookDir()
  • lookDir.rotation = camRef.transform.rotation; tells lookDir to copy the camera.
  • lookDir.Rotate(-lookDir.eulerAngles.x + sidewaysRot.x, 0, sidewaysRot.z);
    • Reverses the x rotation, so that the camera's up and down angle don't affect motion.
    • Leaves the y rotation alone, this is what we wanted the most.
    • Adds the rotation variable sideWaysRot to LookDir's x and z rotations.

    public IEnumerator rotateToCam(int angleOffX = 0, int angleOffZ = 1, float timeset = 0.1f) This function rotates the model based on the camera's rotation.

  • setLookDir();
  • Vector3 tAngle = Vector3.zero;
    • tAngle += lookDir.right * angleOffX;
    • tAngle += lookDir.forward * angleOffZ;
    • These 3 functions create a new Euler Angle by using the camera's rotation as a base.
  • if(tAngle.magnitude != 0) lookRotation = Quaternion.LookRotation(tAngle.normalized , lookDir.up);
    • if the desired Euler Angle exists, it is converted to the Quaternion LookRotation.
  • int t = 0;
  • while(t<10)
    • modelRef.transform.rotation = Quaternion.Lerp(modelRef.transform.rotation,lookRotation,0.1f * Master_Script.gameSpeedTimer); 
    • t++; 
    • yield return new WaitForSeconds(timeset);
      • All this basically just turns the model 10 times towards the desired angle.

And that's the 3D_Movement script. You'll notice mover() and jumper() aren't called. That's because the actual button presses are handled by a different script, so that the Movement script can be used for enemies as well as the player character. The second script will now be explained below, as originally I used these functions alongside the Movement script.

Player_Control
void Start()
  • playRef = this.gameObject;
  • movRef = playRef.GetComponent<Movement>();
    • References to the player object and Movement script are made.

void Update()
if(!Master_Script.ispaused && para==0) everything under Update only works if the game isn't paused and the character isn't stunned.

  • If the character isn't already moving at maxspeed or you're in the air, and you're not stunned, the mover variables are called through movRef.mover(1-4);
//Slowing Down
  • For both horizontal and vertical movement, if there are not buttons being pressed the movementspeed of either direction is Lerped down to 0.
  • movRef.actionMov is set to wether or not you're holding down the shift key.
//Jumping
  • If you're in the air and holding down the jump key, and you have available doubleJumps and are past a certain falling speed:
    • movRef.StartCoroutine(movRef.jumper(120)); you flap your wings.
    • movRef.doubleJumps+=-1;
  • If you are on the ground && tap the jump key, you jump.
    • movRef.StartCoroutine(movRef.jumper()); 
And that's the Player_Conrol script. I've been having a good time working on this new project in the past week and 1/2, and I should have something to show for it very soon. 

No comments:

Post a Comment