Game Coding Complete, Fourth Edition

Category: Programming
Author: Mike McShaffry, David Graham
4.0
This Month Stack Overflow 2

Comments

by anonymous   2019-07-21

If you use the clicklistener you need to let the other actor hold an reference to it to call a method on click. It is not that good to let all Actor know of each other. Use an anonymous way.

There is a "common" system for it in games.

If you really want to use Events, do implement an Event-System. Therefore you have an interface Listen and an Interface Event_Handler. At the start of your game you init one Implementation of the Eventhandler. The interface should at least look like this:

public interface Interface_EventHandler extends Disposable
{
    public void handleEvent(final Event... e);
    public void registerListener(final Interface_Listen listener,
            final Event_Type... type);
    public void unregisterListener(final Interface_Listen... listener);
    public void unregisterAllListener();
    public void unregisterAllListener(final Event_Type... type);
    public void processEvents();
    public void processEvents(final int maxTimeInMS);
}

Okay so now how does it work. The handler has an hashmap with all eventtypes as Key and an list of listeners as Value. So if someone want to notice an event he registers with the registerListerner at the handler for the right Event_Type (Enum). It need to have the interface Listen to get events. Everyone can now push an Event into the handler with the handleEvent(...) method. Or even more than one.. (varargs) ..

Okay that still does not explain how it work. We now have a registered listener (actor for example) and we have events that get into the handler.

Every Rendercycle you call the processEvents() at the hanlder once. That mean that every event that get pushed in at a frame get handled at the next frame. (Asynchronus) While that he iterates over all events and push them to the listeners. Moreover the listener should have a queue too where they put all events and when they are at their .act() they handle the events. (more asynchronus).

Okay here is an Handler i use:

package com.portaaenigma.eventsystem;

import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import com.badlogic.gdx.utils.TimeUtils; import com.portaaenigma.managers.Logger;

public class EventHandler implements Interface_EventHandler { private HashMap> listeners; private LinkedList events;

public EventHandler()
{
    listeners = new HashMap<Event_Type, ArrayList<Interface_Listen>>();
    // add the arraylist for every Eventtype
    for (Event_Type e : Event_Type.values())
    {
        listeners.put(e, new ArrayList<Interface_Listen>());
    }
    events = new LinkedList<Event>();
}

@Override
public void handleEvent(final Event... e)
{
    for (Event event : e)
    {
        events.push(event);
    }
}

@Override
public void unregisterListener(final Interface_Listen... listener)
{
    for (Event_Type e : Event_Type.values())
    {
        for (Interface_Listen interface_Listen : listener)
        {
            listeners.get(e).remove(interface_Listen);
        }
    }
}

@Override
public void processEvents()
{
    while (events.size() != 0)
    {
        // get the first element and delete it
        Event e = events.pop();
        for (Interface_Listen l : listeners.get(e.getType()))
        {
            l.handleEvent(e);
        }
    }
}

@Override
public void processEvents(final int maxTimeInMS)
{
    int startSize = 0;
    if (events.size() != 0)
    {
        startSize = events.size();
        Logger.log("Processing Events: " + events.size());
    }
    long startTime = TimeUtils.millis();

    while (events.size() != 0)
    {
        // get the first element and delete it
        Event e = events.pop();
        for (Interface_Listen l : listeners.get(e.getType()))
        {
            l.handleEvent(e);
        }
        // stop handling if time is up
        if (startTime - TimeUtils.millis() > maxTimeInMS)
        {
            Logger.log("Handled " + (events.size() - startSize) + " Events");
            break;
        }
    }
}

@Override
public void registerListener(final Interface_Listen listener,
        Event_Type... type)
{
    for (Event_Type event_Type : type)
    {
        listeners.get(event_Type).add(listener);
    }
}

@Override
public void unregisterAllListener()
{
    Logger.log("UnregisterAll");
    for (Event_Type e : Event_Type.values())
    {
        listeners.get(e).clear();
    }
}

@Override
public void unregisterAllListener(final Event_Type... type)
{
    for (Event_Type event_Type : type)
    {
        listeners.get(event_Type).clear();
    }
}

@Override
public void dispose()
{
    unregisterAllListener();
    events.clear();
    listeners.clear();
}
}

The interface for all listeners is simple it's just this: public interface Interface_Listen

{
    public void handleEvent(final Event e);
}

Last but not least the event. How can you now send different data? Quiet simple. Have an hashmap out of Strings and Strings and for sure the EventType.

public class Event
{
private Event_Type type;
private HashMap<String, String> m_messages;

public Event(final Event_Type e, final Event_Message... m)
{
    m_messages = new HashMap<String, String>();
    for (Event_Message message : m)
    {
        m_messages.put(message.m_key, message.m_value);
    }
    type = e;
}


public Event_Type getType()
{
    return type;
}

public void addMessages(final Event_Message... m)
{
    for (Event_Message event_Message : m)
    {
        m_messages.put(event_Message.m_key, event_Message.m_value);
    }
}

public String getMessage(final String name)
{
    if (m_messages.get(name) == null)
    {
        Logger.error("Message not found: " + name);
    }

    // if null return an empty string
    return m_messages.get(name) != null ? m_messages.get(name) : "";
}

public void clearMessages()
{
    m_messages.clear();
}
}

Okay i hope this does explain how to implement an EventSystem at a Game. This meight not be the regular way at other Software but in games you queue up the events and handle them once in a Gameloop cycle. Also the listeners do the same.

So in your case. Implement such an handler and register the actors as listener. Sure they need to implement the listener interface and do something with the event. Let the one actor push an event into the handler which directs to the other actor and your are done. And they event dont need to know of each other and it does work for as much actors as you whish. You can even create 1 event for different classes different actors and so on. Usefull for example at mapchange. You push one event with the notice.. "changemap".. and every actor knows he need to stop moving and every subsystem knows that it does need to stop because of an mapchange and so on ...

It seems to be a bit overkill but it has alot of advantages and worth to use even at early stages. I made the misstake and started using it laterly and now i regret it.

Sorry for the bracing. It's not the regular java standart but more clear i think... Sorry for alot of varargs just like it at that point. Meight be confusing.


Literatur:

Game Coding Complete, Fourth Edition Chapter 11

by anonymous   2019-07-21

I would suggest not connecting the physics so directly to the player input, it's not a very extendable architecture.

Have an interface which defines physics properties such as velocity which is implemented by your character object. When the player presses the up-key you should check if the player is on the ground, if they are then set their acceleration to move up. Then have a physics system iterate through a list of every object which implements the physics interface applying gravity and other forces before moving the entities around. This way you can use the same physics code for every entity in your game.

I would also suggest not connecting keyboard input directly to the movement of the player. You can have an interface called 'ICharacterController' which defines a 'nextInstruction' method which returns 'CharacterInstructions' which defines various actions characters can take such as jumping and moving, this can be implemented by an input class. Your character class then holds a reference to a CharacterController so it just calls nextInstruction, this way the character can be controlled by Player Input, AI, or through network communication but still share the same behavior as the player.

Game Coding Complete is a brilliant book to read if you're serious about game programming.

I can also recommend Game Engine Architecture

As you're using XNA you should consider using Game Components

by rootlocus   2017-09-28
While I agree this is a wonderful resources, it is much less about game development than it is about design patterns. Basically, it's a gamedev flavored book about design patterns. A much better books about game development would be Game Coding Complete [0].

[0] https://www.amazon.com/Game-Coding-Complete-Fourth-McShaffry...