Jump to content
  • Advertisement

Dev Blog #02 – Smoothing a path for Terrator

Sign in to follow this  
RoKabium Games

595 views

The last week we’ve been implementing a new enemy in “Something Ate My Alien” called “Terrator”.

He’s much larger than the other enemies, and he’s a worm like creature that we wanted to randomly appear, work its way across the screen and ‘Eat’ our alien if he gets in the way.

Kat created the art, and split it up into sprites for each of the ‘worm’ sections, head, body and tail.

Game-SAMA-Something-Ate-My-Alien-Rokabiu

We didn’t want to create a random path as it was moving, because we wanted more control over him. So we decide to plan out a batch of paths that he should follow and then just pick one at random during spawn. To mix things up a bit, the path should be offset by a random number of pixels, and his start position relative to the path should also change. We also allowed it to be ‘flipped’ randomly to create more variation.

We planned out a path with about 15 or so points, and then used a smoothing algorithm called “Chaikin’s Algorithm” to create a smooth path.

Chaikin is nice and quick, not 100% accurate though, because of the way it works, the result doesn’t pass through the original points, but its good enough for what we needed it for.

Game-SAMA-Something-Ate-My-Alien-Rokabiu

It works by ‘cutting corners’ off each point to create a more smoothed curve, and then repeating 3 or 4 times makes it get smoother and smoother. We found 4 times was good enough to make Terrator animate smoothly in the game.

I found a C# example which helped, here: C# Smoothing Path

Although it was a quick algorithm, the implementation I found actually created quite a bit of C# Garbage, so I looked into re-writing it to remove any Garbage.

Now, rather than creating a new array on every smooth pass, you can pre-create an big array, and then it fills in that one array.

So now it was generating zero Garbage during game play. Yay!!

Game-SAMA-Something-Ate-My-Alien-Rokabiu

And as a bonus, with a bit of extra tweaking, I got it to process faster also. For 15 original points and smoothing 4 times, the original routine took about 1.22ms and allocated 3.3kb of Garbage. My new one took 1.05ms and allocated none.

Here is a video clip of it in game:

Here is the barebones C# code I wrote for Unity, to generate the smooth curve if anyone else might find it useful:

// Setup smooth info
int smoothness = 4;
int NumMainPoints = 15;
Vector2[] smoothPoints = new Vector2[(NumMainPoints - 2) * (int)Mathf.Pow(2,smoothness) + 2];

Vector2 startpoint = new Vector2(25,50);
Vector2 centerPoint = new Vector2(50,50);

smoothPoints[0] = startpoint;
smoothPoints[1] = startpoint + new Vector2(8,0);
smoothPoints[2] = startpoint + new Vector2((8 + 2),0);
smoothPoints[3] = centerPoint + new Vector2(-6.0f, 0.5f);
smoothPoints[4] = centerPoint + new Vector2(-2.0f, -1.5f);
smoothPoints[5] = centerPoint + new Vector2(1.5f, 0.5f);
smoothPoints[6] = centerPoint + new Vector2(5.0f, -2.5f);
smoothPoints[7] = centerPoint + new Vector2(2.5f, -5.5f);
smoothPoints[8] = centerPoint + new Vector2(-3.0f, -3.0f);
smoothPoints[9] = centerPoint + new Vector2(-4.5f, -0.5f);
smoothPoints[10] = centerPoint + new Vector2(-2.5f, 2.0f);
smoothPoints[11] = centerPoint + new Vector2(1.5f, 2.5f);
smoothPoints[12] = centerPoint + new Vector2(5.0f, 2.0f);
smoothPoints[13] = centerPoint + new Vector2(9.0f, 2.5f);
smoothPoints[14] = centerPoint + new Vector2(150, -0.5f);

ChaikinFast(ref smoothPoints, smoothness, NumMainPoints);

private void ChaikinFast(ref Vector2[] smoothPoints, int smoothness, int NumMainPoints) {
    Vector2 lastVector = smoothPoints[NumMainPoints - 1]; // Save last point
    int p = NumMainPoints - 2;
    int factor = 2;
    int lastPoint;
    Vector2 offsetRight, offsetLeft;

    // Loop through mulitple times to smooth
    for (int s=1; s<=smoothness; s++) {
        lastPoint = (NumMainPoints - 2) * factor;
        offsetRight = (smoothPoints[p+1] - smoothPoints[p]) * 0.25f;

        // For each point, replace with 2,
        // with each one being 25% more towards the left and right points
        for (int n=lastPoint; n>1; n-=2) {
            offsetLeft = (smoothPoints[p] - smoothPoints[p-1]) * 0.25f;
            smoothPoints[n] = smoothPoints[p] + offsetRight;
            smoothPoints[n-1] = smoothPoints[p] - offsetLeft;
            offsetRight = offsetLeft;
            p--;
        }

        // Prepare for next loop
        p = lastPoint;
        smoothPoints[p+1] = lastVector; // Copy the end point to the end
        factor *= 2;
    }
}

 

View the full article

 

 
Sign in to follow this  


0 Comments


Recommended Comments

There are no comments to display.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Advertisement
  • Advertisement
  • Blog Entries

  • Similar Content

    • By MiTi
      Dear everyone, this is my newest game, please check out and give me feedback. Thanks for your consideration.

      Overview: Cross n Puzz is a creative and addicting word puzzle game. It not only challenges your brain but also improve memory and other types of cognitive function.

      For IOS: https://itunes.apple.com/app/crossword-puzzle-image/id1435575074

      For Android: https://play.google.com/store/apps/details?id=com.caag.crosswordnpuzzle

      Game trailer: https://www.youtube.com/watch?v=stNuktpJH44&feature=youtu.be
      Crossword Puzzle Image Trailer Official.mp4  
    • By KevDev
      A Unity Asset for customizable lowpoly weapons and shields.
      - Full Version: http://u3d.as/1hvG
      - Free Version: http://u3d.as/1jZV
      You can get the character model used in the video here (for FREE!): http://u3d.as/1kP7
       

      View full story
    • By KevDev
      A Unity Asset for customizable lowpoly weapons and shields.
      - Full Version: http://u3d.as/1hvG
      - Free Version: http://u3d.as/1jZV
      You can get the character model used in the video here (for FREE!): http://u3d.as/1kP7
       
    • By KevDev
      [UNITY ASSET]
      Customizable lowpoly weapons and shields.
      - Full Version: http://u3d.as/1hvG
      - Free Version: http://u3d.as/1jZV
      You can get the character model used in the video here (for FREE!): http://u3d.as/1kP7
       
    • By mtjscott
      Hey, so i've created a disk in unity (2D mobile) that will be shot forward if you drag it back and the further you drag it from the start point the more force will be applied to the impulse similar to the 8ball pool drag to shoot mechanic on miniclip. However, when I applied a script that allows the main camera to follow the ball it broke the mechanic since the balls position is calculated through the camera in world space. So I created a bool that locks the camera in place until the ball is released so the calculation would happen before the camera starts to move. This works however the ball now rubber bands back and forwards close to the start position.
       
      If anything needs more explaining then i'd be glad to do so. I've only been coding for about a week so you'll have to bare with me. Any help is appreciated. Thank you very much.
       
      Here's What happens:
      https://gyazo.com/f211e50f32ac59437a93dad7295a14be
      (screencap gif of the game viewer)
       
      Here is the shoot script:
      using System.Collections; using System.Collections.Generic; using UnityEngine; public class Shoot : MonoBehaviour { [SerializeField] GameObject Disc; [SerializeField] float multiplier; Vector3 initPos; private Rigidbody2D rb; public static bool ballIsReleased = false; bool recordingDistanceDragged = false; private void Start() { rb = gameObject.GetComponent<Rigidbody2D>(); initPos = transform.position; } void OnMouseDrag() { recordingDistanceDragged = true; if(recordingDistanceDragged == true) { transform.position = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 10)); } else { transform.position = initPos; } } void OnMouseUp() { ballIsReleased = true; } private void FixedUpdate() { if(ballIsReleased == true) { rb.AddForce((initPos - transform.position) * multiplier, ForceMode2D.Impulse); Debug.Log("ball is released"); recordingDistanceDragged = false; } else { ballIsReleased = false; } } }  
      Here is the camera follow script:
      using System.Collections; using System.Collections.Generic; using UnityEngine; public class CameraFollow : MonoBehaviour { private Vector2 velocity; public float smoothTimeY; public float smoothTimeX; public GameObject player; private void Start() { player = GameObject.FindGameObjectWithTag("Player"); } private void FixedUpdate() { if (Shoot.ballIsReleased == true) { Debug.Log("camera can move"); float posX = Mathf.SmoothDamp(transform.position.x, player.transform.position.x, ref velocity.x, smoothTimeX); float posY = Mathf.SmoothDamp(transform.position.y, player.transform.position.y, ref velocity.y, smoothTimeY); transform.position = new Vector3(posX, posY, transform.position.z); } } }  
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!