Panda3D: Toon Trouble

Game demo made in Panda3D (Python).

png


Overview


Toon Trouble was a three-month long project I worked on for my computer science course. This project originally started as a few sample programs I created to get myself experienced in different features Panda3D had to offer. There’s a lot of unused modules within the code, and a lot of features disabled, only because I moved on from working on those features and moved on to something newer. Some of the other smaller programs may have a few series of errors and bugs that may result in unrunnable code… I’ll try to get to that… sometime in the future… when time isn’t a burden…

You can find the open source repo for Toon Trouble here. although no promises it’ll work on your machine out of the box!

With the great amount of support from a few of my online friends, this project wouldn’t be where it’s standing today without them.

Preface


Before getting into the juicy stuff, I first need to tell you how it all started in the first place, and how I got to this point. My first few projects with Panda3D weren’t related to this game at all, but more so over the features the Panda3D game engine had to offer for game developers. I started out with a basic, rudimentary assignment of making a powerpoint – listing numerous features devs can toy around with.

What I Learned


I learned a lot more than I expected to learn from this project. I brushed up on a few concepts I didn’t quite grasp yet, and learned some ‘formal’ definitions for terms I couldn’t really describe from my head.

  • Python.

    • Before taking this challenge, I had seldom experience working with the Python syntax. I had previously worked with the programming language before, however I wasn’t confident at all with the syntax and making programs from the ground-up.
  • Panda3D.

    • While I’ve previously utilized the Panda3D game engine before this project, I had never affixed my focus into reading the manual. Because of this, I didn’t actually know what I was doing, – or at least the formal name of what I was doing. I copied patterns and logically grouped things together knowing that “If I do this… it’ll do that.”.

    • After studying the manual in more detail, I learned a lot of the “formal” names of the classes I was actually instancing, and more importantly, learning all of the capabilities of such classes with the Panda3D API.

    • (I’m personally proud of this one) Adding and applying collision nodes on *.egg models (Panda3D Model Extension). Because of this, I could technically import any model (typically .obj) from any game (from like The Models Resource), apply proper collisions, and implement it as a NodePath in-game. Here’s an example of a collisions node in an egg file.

      <Group> polygon47-coll {
      <Collide> { polyset descend }
      <VertexPool> polygon47Shape.verts { ... }
      }
      
  • Computer Science Principles.

    • While studying the Panda3D manual, I learned about a few key terms that are significant in relation to the computer science field.

      • Scene Graph: A Collection of nodes in a graph or tree structure; defines what’s being rendered.

      • Finite State Machine (FSM): An abstract machine that can only be in one of many finite states at a given time, restrictions can be set regarding entering states

      • Lerping: Slang for “linear interpolation”. Linear interpolation is ultimately useful to interpolate (or insert) a node at a midpoint between node X and node Y.

      • Sequences: Used to form step-by-step functions that play in order; can also be paused/stopped

      • Intervals: Used to move from position X to position Y in T time (seconds). Like keyframes.

      • Tasks: Special functions that are called once each frame, ran one at a time within the main thread.

  • And a bit of animating in Maya.

Challenges I Faced


Projectile/Cannon Physics

png

Projectile/cannon physics weren’t the easiest to fix. When the cannon incremented/decremented its rotation value, the projectile’s hit calculation (where the trajectory would be shot) didn’t update correctly and would cause the projectile to be shot with some really high pitch value. It wasn’t some simple error like assigning the setH to getP, or by somehow accidentally grabbing the P value when it shouldn’t been grabbed.

Enemy AI

Enemy AI was a bit strange to wrap my head over. Panda3D is said to have an AI integration, but it doesn’t quite work as expected. PANDAI is Panda3D’s AI implementation, however it was deprecated in recent versions of Panda3D. Because of this, I had to plan out how exactly I could implement simple AI code for the enemies.

Overcoming These Challenges


Projectile/Cannon Physics

This one is quite hard to explain, but in a nutshell, the HPR value was getting the wrong value.

png

Enemy AI

png

Disyer and I planned out a 6x4 grid in front of the headquarters where the enemies approached. The enemy would then pick and occupy a random spot on the grid. If they picked a slot that was already occupied, they’d just select another random spot. After picking a random slot to occupy, the enemies were then to face the Headquarters and attack it by using sequences and time intervals.

from panda3d.core import *
import random

class EnemyGrid(object):

    def __init__(self, startX, startY, z, xIncrement, yIncrement, xSize, ySize):
        self.positions = []
        self.occupied = []
        self.xSize = xSize
        self.ySize = ySize

        for i in range(xSize):
            self.positions.append([Vec3(startX + (i * xIncrement), startY + (j * yIncrement), z) for j in range(ySize)])
            self.occupied.append([None for j in range(ySize)])

    def getMaxEnemies(self):
        return self.xSize * self.ySize

    def occupyRandomSpot(self, token):
        for y in range(self.ySize - 1, -1, -1): # Reverse Y loop
            freeIndices = [x for x in range(0, self.xSize) if self.occupied[x][y] is None]

            if freeIndices:
                x = random.choice(freeIndices)
                self.occupied[x][y] = token
                return self.positions[x][y]

    def releaseSpot(self, token):
        for x in range(0, self.xSize):
            for y in range(0, self.ySize):
                if self.occupied[x][y] == token:
                    self.occupied[x][y] = None

In order to determine how much HP they lost from being hit with a trajectory (being the pie), I chose a random value between 30 hp lost and 50 hp lost. I also applied the same rule when it came to how much damage the enemies (cogs) did to the building (Toon HQ)

   def takeDamage(self):
        if self.currHP <= 0:
            return

        if not self.lastAnimation:
            self.lastAnimation = self.getCurrentAnim()

        hpTaken = random.randrange(30, 50)
        self.currHP = max(0, self.currHP - hpTaken)
        self.showHpText(-hpTaken)
        self.updateHealthBar()

        if self.hurtSequence:
            self.hurtSequence.pause()

        if self.currHP > 0:
            self.hurtSequence = Sequence(
                Func(self.setTunnelRunning, False),
                Func(self.getLeftHand().getChildren().hide),
                Func(self.getRightHand().getChildren().hide),
                ActorInterval(self, 'pie-small-react'),
                Func(self.getLeftHand().getChildren().show),
                Func(self.getRightHand().getChildren().show),
                Func(self.loop, self.lastAnimation),
                Func(self.setTunnelRunning, True)
            )
        else:
            self.hurtSequence = Sequence(
                Func(self.pauseTunnel),
                self.getExplosionTrack(),
                Func(self.announceDead)
            )

        self.hurtSequence.start()

What I Didn’t Accomplish


Configuration Files

I wanted to have an interactive configuration file where the user could toggle on and off different attributes of the game, such as a debug mode that included an injector, frame meter, shading, etc. Maybe I’m just a novice in python, but the entire implementation of importing attributes from JSON files was too tricky for me to implement. In addition, I ran out of time from the deadline to implement this feature.

Loading Screen

All of the modules that were required to be loaded in were all in their individual classes as loader functions. I wanted to refactor this implementation and make a class that were to load everything at once, and show the progress in the form of a loading bar.

Dialogue Cutscene

Originally, I wanted to make it where there would be some introductory dialogue when the game started, introducing the plot of the game and the instructions to play. However, implementing the code to generate the DNA for the character (that would be in the dialogue scene) got pretty convoluted to do. I worked on this random little Toon HQ animation that would’ve played at the beginning cutscene. png

Powerups

I REALLY wanted to implement power-ups into the game. I imagined a scenario where you’d have a pie meter fill up, and once it reached the top, it’ll give the player a random power-up to use. png

Miscellaneous


Before developing Toon Trouble, I initially developed some sampler programs to get myself use to working with Panda3D. Here’s some older development screenshots of me playing around with Panda + Early ‘Toon Trouble’ screenshots.

I also created a powerpoint about different CS topics I learned about while looking over Panda3D’s manual. If you’re interested, you can check that out here.

Avatar
Erica Miller
Student, Freelancer

I’m me.

Related