Your Logo

About Scrapper

Scrapper is a topdown twin stick shooter, which unfortunately suffered a lot of scrapped ideas. Luckily the core gameplay stayed, shoot a lot of stuff and try not to die!

Unity C# 8 weeks

Trailer video:

My contribution:

Player movement

Your GIF description

I have spent quite some time making this movement satisfying, I wanted the movement to be smoothed out a little bit but what kept happening is that it felt like you were sliding on ice which is obviously not ideal, that is why I chose to only have it smoothed in the first part of the movement, which turned out to be exactly how I wanted it. It also helped that I used a character controller instead of a normal rigid body because, in my opinion, it feels a lot better.


View whole script.

    // get keyboard inputs
    float x = Input.GetAxisRaw("Horizontal");
    float z = Input.GetAxisRaw("Vertical");

    // get orienter gameobject refrence
    GameObject camera = _CameraRig.transform.GetChild(0).gameObject;
    GameObject orienter = camera.transform.GetChild(0).gameObject;

    // set "move" (vector3) var to be relative to orienter
    orienter.transform.localRotation = Quaternion.Euler(-camera.transform.eulerAngles.x, 0, 0);
    _move = orienter.transform.right * x + orienter.transform.forward * z;

    // only smooth movement in the first part of the linear keyboard input
    if (_move.magnitude > 0.3f)
    {
       _move.Normalize();
       currentZ = _Wheel.transform.eulerAngles.z;
    }

    // set velocity
    currentMoveVelocity = Vector3.SmoothDamp(currentMoveVelocity, _move * _Speed, ref moveDampVelocity, _MoveSmoothSpeed);

    // apply gravity
    _Controller.Move(Vector3.down * 2 * Time.deltaTime);

    //only move if grounded
    if (_Controller.isGrounded)
    {
       _Controller.Move(currentMoveVelocity * Time.deltaTime);
    };

            

Camera controller

This is quite a simple camera controller, it simply follows the player around with a little bit of smoothing and is able to rotate 90 degrees at the click of a key. the "dummycam" is only used for screen shake effects, because the camera is smoothed, when I did a screenshake it would return to the wrong position, so the dummycam is always located at where the camera should return to.


View whole script.

    private void Update()
    {
        // check distance to player
       float distance = Vector3.Distance(transform.position, _PlayerTransform.position);

       // set camera move speed
       _MoveSpeed *= distance / _SmoothTime;

       // only move when the distance to the player is too large
       if (distance > _MaxDistanceBeforeMoving)
       {
          _isMoving = true;
       }

       if (_isMoving)
       {
          // set target position
          Vector3 targetPos = _PlayerTransform.position + new Vector3(0, _Offset.y, 0);

          // smoothly go to target position
          transform.position = Vector3.SmoothDamp(transform.position, targetPos, ref _velocity, _SmoothTime, _MoveSpeed);
          _DummyCam.transform.position = targetPos;

          // know when to stop moving
          if (_isMoving && distance < _MaxDistanceBeforeMoving + 0.1f)
          {
             _isMoving = false;
          }
       }

       // move the camera when one of the buttons is clicked
       if ((Input.GetKeyDown(_CamMoveKeys[0]) || Input.GetKeyDown(_CamMoveKeys[1])) && !_camMoveing)
       {
          // set direction for movement and start coroutine
          int direction = (Input.GetKeyDown(_CamMoveKeys[0]) ? 1 : (Input.GetKeyDown(_CamMoveKeys[1]) ? -1 : 0));
          StartCoroutine(MoveCam(direction));
       }

       // reset position
       if (Input.GetKeyDown(_CamResetKey))
       {
          StartCoroutine(ReturnToOriginalPos());
       }
    }

            

Player weapon system


View whole script

    private IEnumerator Shoot(Quaternion rotation)
    {
       _shooting = true;

       //shake cam
       _gameManager.CameraShake(0.06f);

       //normal weaponns
       if (!_WeaponType.IsSpecialWeapon)
       {
          Quaternion rot = _GunHolder.transform.rotation;
          //single shot
          if (_WeaponType.BulletAmount == 1 && !_WeaponType.BurstFireWeapon)
          {
             GameObject flash = Instantiate(_MuzzleFlash, _GunHolder.transform.position, rot * Quaternion.Euler(0, -90, 0));
             Destroy(flash, 0.1f);
             if (_WeaponType.AmmoType == WeaponShootBehaviours.BulletType.pistol)
             {
                FindObjectOfType<Audio>().PlayPistolShot();
             }
             else
             {
                FindObjectOfType<Audio>().PlayARShot();
             }
             GameObject bullet = Instantiate(_BulletPrefab, _GunHolder.transform.position, rotation);
             _BulletAmount--;
          }

          //shotgun shot
          else if (_WeaponType.BulletAmount > 1)
          {
             Tuple<Vector3[], Quaternion[]> result = MakeBulletSpread(_WeaponType.BulletAmount, _WeaponType.BulletSpreadAmount, _GunHolder.transform.position, rotation);
             Vector3[] spreadPositions = result.Item1;
             Quaternion[] spreadRotations are result.Item2;

             for (int i = 0; i < _WeaponType.BulletAmount; i++)
             {
                GameObject bullet = Instantiate(_BulletPrefab, spreadPositions[i], spreadRotations[i]);
             }
             FindObjectOfType<Audio>().PlayShotgunShot();
             yield return new WaitForSeconds(0.05f);
             GameObject flash = Instantiate(_MuzzleFlash, _GunHolder.transform.position, rot * Quaternion.Euler(0, 0, 0));
             Destroy(flash, 0.1f);
             _BulletAmount--;
          }

          //burst shot
          else if (_WeaponType.BurstFireWeapon)
          {
             for (int i = 0; i < _WeaponType.BurstFireShotAmount; i++)
             {
                GameObject bullet = Instantiate(_BulletPrefab, _GunHolder.transform.position, rotation);
                yield return new WaitForSeconds(_WeaponType.BurstTimeBetweenShot);
                _BulletAmount--;
             }
          }
       }
    }