4
\$\begingroup\$

I've been busy trying to create a level editor for a tile based game i'm working on. It's all going pretty fine, the only thing i'm having trouble with is creating a simple event system.

Let's say the player steps on a particulair tile that had the action "teleport" assigned to it in the editor. The teleport string is saved in the tile object as a variable. When creating the tilegrid an actionmanager class scans the action variable and assigns actions to the variable.

    public static class ActionManager
{

    public static function ParseTileAction(tile:Tile) {

        switch(tile.action) {

            case "TELEPORT":

            //assign action here

            break;
        }

    }


}

Now this is an collision event, so I guess I should also provide an object to colide with the tile. But what if it would have to count for collision with all objects in the world? Also, checking for collisions in the actionmanager class doesn't seem very efficient.

Am I even on the right track here? I'm new to game design so I could be completly off track. Any tips on how handeling and creating events using an editor is usually done would be great. The main problem i'm having is and efficient way to read out and react to the given actions in the editor.

Thanks in advance.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ What data format are you using? (XML?) I'm not familiar with Action Script. Sounds like this is going to be data driven. \$\endgroup\$ Commented Mar 17, 2011 at 13:23

4 Answers 4

1
\$\begingroup\$

I'm not really familiar with action script, but it looks pretty familiar to C# and I think I understand your question/dilemma.

It might be beneficial to have certain triggers (like when a player steps on the tile or when anything steps on the tile). These triggers could be part of a tile or possibly part of a map.

Then you assign actions to do to when these triggers fire/occur.

Basically, this event system is split into two parts. One part (the trigger) alerts the action to be executed. The second part actually executes the action and possibly checks for extra conditions.

If you did this then your map editor would be able to assign triggers to a tile (or a map). Then you could specify an action to execute for that specific trigger. So your trigger for the teleport tile would do the collision check (or hook into some physics/collision system in order to be notified when a collision occurred with the tile) and then fire off the teleport action.

PS: I would consider having the action being specified with more than just a string (or at least a string that can also have arguments in it). How would you specify where that tile teleports the player if the string has to be "TELEPORT". Just some food for thought.

Also, part of your question (near the end) seems to be missing? "The main problem i'm having is the [missing part?]"

\$\endgroup\$
1
  • \$\begingroup\$ Woops! replaced the missing part, was having some problems because I was on a crappy 3G network. The trigger method was what I was going for :) I just don't know how to translate the idea to code. I guess the type of trigger and the action aswell would be defined in a string. But using a huge switch statement to react to the given trigger/action like my example seems inefficient. Perhaps an example would example further? Thanks :) \$\endgroup\$
    – omgnoseat
    Commented Mar 16, 2011 at 18:55
0
\$\begingroup\$

Action/Result is a function of a bunch of variables e.g. f(x,y,z ... )

First, establish what the x, y, z etc are and then what the resulting action will be. Think of x, y, z as tile (its position, state etc.), current level, current environment variables etc. Then decide who will act on the action. Will it be the tile or a controller OR both? Once you have answered the first two questions, you will know what variables/values you need to send back to the tile or send to a controller.

\$\endgroup\$
0
\$\begingroup\$

In your map there should be "Zones"/"Triggers". In my game my triggers have an action field, and a parameter field specifying how the action should be done.

Example: The player, when entering a teleporter-trigger, should only teleport when there is a button activated. The condition should be tested inside the engine. Your trigger could look like this: Type: Trigger Region: ... Action: Teleport Conditions: Button1.Active == true Parameter: 100, 200 (the target coordinates)

Your game should therefore have condition functions, and Actions (with parameters). That's the exact way it's handled in all major games. Warcraft3 engine, the SourceEngine, Unreal engine... Warcraft3 being a perfect example. Its script editor consists of Events, Conditions and Actions. Look it up, maybe you can get inspiration from it.

Tip: I don't know the abilitys of actionscript but try to parse the level data into static arrays. the iterate over them every frame to see if anything is triggered. In my game there is also a field that describes how often this entitys logic should be updated.

Ask if anything's unclear, I'm happy if I can help.

\$\endgroup\$
0
\$\begingroup\$

The trick to this is really just finding a way to balance the amount of data parsing and hard-coded behavior. I'm sure that a teleporter is not the only type of behavior you'd like to add to your tile. Ideally you'll be able to add new behaviors quickly, and have an obvious way for describing them in a data format.

You didn't provide a lot of details on your tile class, so I'm gonna make some crap up. Let's assume you have some sort of base GameObject class, which has some basic variables like position and group. I'm assuming GameObjects are things that are generally dynamic in your game. Things like static scenery wouldn't fall under this category. Group is just something I tacked on to illustrate the effect of checking for just the player, all NPCs, etc.

public class GameObject {
    public static const PLAYER_ONLY:int = 1;
    public static const ALL_CHARACTERS:int = 2;
    public static const ALL_ENEMIES:int = 3;
    public static const ALL_FRIENDLY_NPCS:int = 4;
    public static const INTERACTABLES:int = 42;
    public static const BLOCKS:int  = 99;
    public static const TREASURE_CHESTS:int = 20384293;

    public var Group:int;
    // assuming you have some Point class encapsulating X/Y coordinates
    public var position:Point;
}

Now for a tile class. I'm using a library called as3signals (alternative library: TurboSignals). It basically allows you to create C#-style delegates to get around the hassle of having to use Flash's standard event system with strings and EventDispatcher. You can accomplish the same thing using the EventDispatcher method, but it's much less sexy.

The way it works is you have fields in your class that are of type Signal. Each signal has an add() function, which behaves like eventDispatcher.addEventListener, except that the listener you are adding is for that specific signal, and nothing else. It makes your events type-safe, so it's good for events where you know you will absolutely need them, such as OnObjectEnter and OnObjectLeave. Sure, you don't get the flexibility of arbitrarily defining event string literals to shoot out into the ether, but these things are pretty easy to use. And nothing stops you from using EventDispatcher in conjunction with signals.

One thing that's nice about as3signals is you can define the type of argument that you send with the dispatch. In the case of the Tile, it will send a GameObject parameter, so that whoever is interested in the event will know which game object entered the tile.

I just created two signal slots: OnObjectEnter and OnObjectLeave. These serve to notify other objects when a GameObject has entered/left the tile. This is what we will use to implement the teleporter.

public class Tile {
    // Useful to know when GameObjects enter and leave this tile
    public var OnObjectLeave:Signal = new Signal(GameObject);
    public var OnObjectEnter:Signal = new Signal(GameObject);
    public var position:Point;

    // Use Vector.<T>s, they're faster than plain Arrays
    private var properties:Vector.<TileProperty> = new Vector.<TileProperty>();

    public function addProperty(property:TileProperty):void {
        properties.push(property);
        property.attachToTile(this); // Because we have no double dispatch :(
    }

    public function Update(deltaTime:Number):void {
    // This update function is kinda dumb, it just serves the purpose of 
    // having a starting point for reacting to an object entering the tile
        if (someIndicationThatThisTileWasEntered) {
            OnObjectEnter.dispatch(getObjectThatJustEntered());
        }

        if (someIndicationThatThisTileWasLeft) {
            OnObjectLeave.dispatch(getObjectThatJustLeft());
        }

        for (var property in Properties) {
            if (property.NeedsUpdating) {
                property.Update(deltaTime);
            }
        }
    }
}

You'll also see the properties field in there, a vector of TilePropertys. If you want to code special behavior and be able to duplicate it in multiple areas (such as a teleportation behavior), then you subclass this. It provides an overridable Update(deltaTime) function, information about the parent tile and an overridable function that notifies you when the parent changes (good for object pools), and a bool for toggling whether it gets Update() called on it every frame or not.

I put an update function in the Tile, but it's really just to have some point in this example where you can follow the chain of collision detection -> trigger teleportation. In a real game I would leave the responsibility of firing the signals to some larger collision system class.

public class TileProperty {
    public var NeedsUpdating:Boolean;

    private var parentTile:Tile;
    protected function get ParentTile():Tile { return parentTile; }

    public final function attachToTile(tile:Tile):void {
        var oldTile:Tile = parentTile;
        parentTile = tile;
        onNewParent(oldTile);
    }

    protected function onNewParent():void {
        // Override this
    }

    public function Update(deltaTime:Number):void {
        // Override this
    }
}

So now we've got a TeleportTriggerProperty class, and this is where the teleportation actually happens. When OnNewParent() is called, it adds a listener (teleportObject) to the OnObjectEnter signal. That way, whenever an object enters the tile that this TileProperty is attached to, it can teleport that object.

public class TeleportTriggerProperty extends TileProperty {
    private finalDestination:Point;

    public function TeleportTriggerProperty(destination:Point, group:int) {
        finalDestination = destination;
    }

    override protected function OnNewParent(oldTile:Tile):void {
        // I'm not sure if trying to remove a null listener, or
        //   a non-existent listener throws exceptions
        // If it does, then wrap it in try-catch.
        // Or better yet, modify the library so it DOESN'T :P
        oldTile.OnObjectEnter.remove(teleportObject);
        parentTile.OnObjectEnter.add(teleportObject);
    }

    private function teleportObject(thing:GameObject):void {
        // Maybe in some scenario this OnObjectEnter could get called if a 
        //   chest gets placed on the tile, so it's good to confirm the Group.
        // And maybe you want to be able to configure it to only teleport
        //   the player, or only enemies.
        if (thing.Group == GameObject.ALL_CHARACTERS) {
            // BAM! Teleported. Insert special effects as necessary.
            thing.position = finalDestination;
        }
    }
}

So now you've got all this infrastructure. How does this apply to the level editor?

Well, notice that really the only thing that a TeleportTriggerProperty needs to work is a Point and a Group ID. These are the two fields that you can expose in your level editor. Then you can presumably export your level structure in XML or some other data format to look like this (I used XML, since its hierarchical nature makes sense in this scenario, and as3 parses it natively):

<Tile>
    <Position>24,42</Position>
    <Properties>
        <TeleportTriggerProperty destination="42,24" group="2" />
        <TriggerMusicProperty track="NeverGonnaGiveYouUp.mp3" />
        <BrutallyMurderPlayerInFutureProperty time="3m30s" />
    </Properties>
</Tile>

It probably isn't ideal to store a big XML hierarchy for each and every tile in your game, but this was the most straightforward example I could think of.

Then in your parser routine, you could iterate through and construct your TileProperties as needed. You could conceivably automate this process with serialization. I haven't done serialization in as3 though, so that's homework for you if you're interested. If you don't use serialization, I'd recommend having a few separate classes responsible for parsing specific types of elements (a GameObject parser, a Tile parser, and maybe even a separate TileProperty parser).

I called this thing a TileProperty in the hopes that it could possibly accomodate both direct actions like your teleporter problem, or tile effects, such as diminishing the health of whatever character stands on it every few seconds. But, it may be better to implement a couple different systems to get done what you want to. It depends on your game.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .