Tuesday, August 30, 2011

Focus on Game Logic

The focus is what gives the game programmer his power.
It took me years to get to the point where I finally realized that most 2D games have a very simple set of code necessary from the graphics and collision ends. All you need is the ability to draw images and detect collisions, and maybe play sounds (qed eh), the rest is all game logic.

It binds the games together
The game logic is the truly unique and interesting pieces of code, had I known this long ago or listened to people when they told me to focus on making a game and not just the graphics and structural code, I would have had so much more experience programming game logic. Alas, it is what it is and I need to churn out game logic code like there's no tomorrow in order to catch-up on missed opportunities.

Use the focus Luke, let go your graphics!
So a word to the wise, frequently implement as many different types of game logic code as possible. This will help familiarize you with the implementation problems and challenges and help you flush out ideas much more quickly in the future. Believe me when I say I've spent enough time coding to realize the first time is always the hardest when implementing something you've never done. Each time afterwards becomes much easier, so much so that eventually you can implement it as fast you are able to type.

Not this crude matter!
By game logic, I am referring to things like scoring, inventory, turn based battles, tech trees, quest systems and puzzles etc... Also get into level design early, the hardcore performance heavy and resource intensive code doesn't matter as much as being a good content creator. Sadly these are things I never really got to implement, I was too busy focused on the structural OO design and making a complete engine package or more and more advanced graphics and 3D work. Recently I've been trying to undo years of focusing on the wrong thing. I was in the whole boat of the having a million small projects but none ever got finished.

You're my only hope
As many people will tell you one finished polished game is better than thousands of unfinished ones. This is due to the myriad things needed to polish off a game. Down to minute details that you don't anticipate like crouching and reloading at the same time. Things like balancing and making sure the game is fun and the shotgun isn't too powerful or too weak, the bfg is hard enough to get and you don't get it till later. All those things add up and sometimes are so specific to your game design that you would never think about it until you've implemented game X at least once.

Good Luck!

P.S. I'd love to hear of others experiences and advice when implementing game logic. And some good coding exercises to help refine your skills to make game making easier!

Thursday, August 18, 2011

Deceptively Simple Prototypes

I decided to work on some very simple types of snippets related to game mechanics. Small little almost but not quite games! Definitely not polished.

This one I came up with is insanely simple. It's only purpose is just to focus on a process for prototyping different game mechanics. This isn't really a mechanic you would directly use as-is in a game. The core mechanic here being "click" the moving re-sizing circle as close to the center as possible to get maximum points.

Evolution
I tend to like exercises to come up with ways to change a currently working game or in this case snippet to turn it into something else. To sort of evolve it into it's own thing. There are a number of things I could add or change to enhance this and eventually make it into a game. It would be a casual arcade style game. I am not sure how well this idea of taking a simple mechanic and expanding on the gameplay works to create a fully polished game, but in my opinion it's better than just trying to brainstorm and come up with a game even if you're a designer and are good at visualizing the end product.

Prototypes
From my perspective it's definitely worth it. The experience of coding any gameplay mechanics no matter how small is useful. It forces you to turn the ideas in your head into something tangible and to work out coding issues you WILL have later on in development. Almost every game development team I have been on has always focused on the graphics and architecture first. The successful teams either use an existing engine or separate the graphics and architectural code from the game logic such that they are independent.

Your real drive is to get to the good stuff, the fun aspects of game making, the actual design. Coming up with battle systems, interesting weapons, big explosions, and detailed adventure areas, using all your creative juices. I have been tap-dancing around writing any game logic code for years. But I have always found small simple examples to be the best way to explore new areas in coding. So I am pushing myself to create as many small examples of different types of mechanics as possible. And I will of course post them here.

The Snippet
This snippet wasn't hard to get setup and rendering, one of the reasons I enjoy using Java for game development. It's basically create a window and a thread that continuously renders to the screen capture input via mouse position and do stuff when the buttons released.

Here's the 200+ line snippet.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.util.HashSet;
import java.util.Set;

import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class SimpleGame extends JComponent {

    public static class ScoreEffect {

        private static final double MAX_TIME  = 300;

        private final long          starttime = System.currentTimeMillis();

        private final int           score;

        private final int           x;

        private final int           y;

        protected ScoreEffect(int score, int x, int y) {
            this.score = score;
            this.x = x;
            this.y = y;
        }

        public void draw(Graphics2D init) {
            Graphics2D g = (Graphics2D) init.create();
            long cur = System.currentTimeMillis();
            double scale = ((cur - starttime) / MAX_TIME) + 1;
            g.translate(x, y);
            g.scale(1.5 * scale, 1.5 * scale);
            g.drawString(String.format("+%d", score), 0, 0);
            g.dispose();
        }

        public boolean isMax() {
            return System.currentTimeMillis() - starttime > MAX_TIME;
        }

        public static ScoreEffect valueOf(int score, int x, int y) {
            return new ScoreEffect(score, x, y);
        }
    }

    private static final int       MAX_HIT_COUNT = 20;
    private double                 direction     = 1;
    private double                 x             = 0;
    private double                 y             = 0;

    private double                 rsize;
    private int                    mouseX        = 0;
    private int                    mouseY        = 0;

    protected int                  score;

    private int                    hitCount      = MAX_HIT_COUNT;

    private boolean                isGameOn;

    private final Set<ScoreEffect> scores;

    public SimpleGame() {
        this.setPreferredSize(new Dimension(300, 200));
        this.setDoubleBuffered(true);

        scores = new HashSet<ScoreEffect>();

        (new Thread() {
            Object lock = new Object();

            @Override
            public void run() {
                synchronized (lock) {
                    while(true) {
                        repaint();

                        checkLooseCondition();

                        try {
                            lock.wait(10);
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                }
            }
        }).start();

        Dimension size = getPreferredSize();
        if(x == 0) {
            x = size.width / 2;
        }

        if(y == 0) {
            y = size.height / 2;
        }

        addMouseMotionListener(new MouseMotionAdapter() {
            @Override
            public void mouseMoved(MouseEvent e) {
                mouseX = e.getX();
                mouseY = e.getY();
            }
        });

        addMouseListener(new MouseAdapter() {
            @Override
            public void mouseReleased(MouseEvent e) {
                if(!isGameOn) {
                    isGameOn = true;
                } else {
                    double distance = Math.pow(mouseX - (x + rsize / 2), 2)
                            + Math.pow(mouseY - (y - rsize / 2), 2);
                    if(distance <= 400) {
                        int addPoints = (int) (10 * (distance / 400.0d)) + 1;
                        scores.add(ScoreEffect.valueOf(addPoints,
                                e.getX(),
                                e.getY()));
                        score += addPoints;
                        x = 0;
                        direction = 1;
                    }
                }
            }
        });
    }

    protected void checkLooseCondition() {
        if(hitCount <= 0) {
            isGameOn = false;
        }
    }

    @Override
    public void paintComponent(Graphics init) {
        Graphics2D g = (Graphics2D) init.create();

        if(isGameOn) {
            rsize = Math.abs(getWidth() / 2 - x);

            rsize /= getWidth() / 2;

            rsize = 1 - rsize;

            rsize *= 20;

            if(x <= 0) {
                direction = 1;
            }

            if(x >= getWidth() - rsize) {
                --hitCount;
                direction = -1;
            }

            x += direction * 2;
            g.setColor(Color.black);
            g.drawOval((int) x, (int) (y - rsize / 2), (int) rsize, (int) rsize);

            g.drawString(String.format("Score: %d", score), 10, 25);
            String hits = String.format("Hits Left: %d/%d",
                    hitCount,
                    MAX_HIT_COUNT);
            g.drawString(hits, getWidth() - 10
                    - g.getFontMetrics().stringWidth(hits), 25);

            Set<ScoreEffect> remove = new HashSet<ScoreEffect>();
            for(ScoreEffect e : scores) {
                e.draw(g);
                if(e.isMax()) {
                    remove.add(e);
                }
            }

            scores.removeAll(remove);
        } else {
            String gameon = "Game Over - click to restart!";
            g.drawString(gameon, getWidth() / 2
                    - g.getFontMetrics().stringWidth(gameon) / 2, getHeight()
                    / 2 - g.getFontMetrics().getDescent());

            String lastScore = String.format("Last Score: %d", score);
            g.drawString(lastScore,
                    getWidth() / 2 - g.getFontMetrics().stringWidth(lastScore)
                            / 2,
                    getHeight() / 2 - g.getFontMetrics().getDescent() + 30);

        }
        g.dispose();
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                JFrame frame = new JFrame("Hit Me");
                SimpleGame g = new SimpleGame();
                frame.add(g);
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

}

Wednesday, August 10, 2011

Current snippets update

Still working on getting some useful mechanics snippets out. I want to focus on posting snippets that are the most helpful and insightful into some of the problems in coding game mechanics and game logic. I am working on the jump mechanic and others for platformers. What I am finding more and more is that most platformers have very specific code for handling their basic mechanics, since this is the first time I've actually attempted to write platformer movement code it's taking a while to get right. So today I will am posting my current progress it works in the sense that you can add platforms and jump from platform to platform but there are a number of subtle issues that you wouldn't want in the end.

To Fall or not to Fall
All I really wanted to show was a jump mechanic then I decided to add gravity since it is needed in order to do jumping, and then I figured we need platform collisions to stop the player and provide a base for jumping. The problem I am facing as of this writing is that of gravity as a constant acceleration in a direction (at this point it's mainly in the positive y direction or down but it's not hard and set in code) I am thinking I am trying to do too much at once perhaps it's better to do some daily progress. I don't expect the progress to be that great as it is I don't have much time to spend on doing these snippets and often second guess posting some times to hold out for a better idea or a more critiqued snippet and post.

Why I never tried coding a platformer before?
I've been programming games for a long time now 10+ years and while I did originally start a project similar to a platformer at that time I did not know the necessary physics to code the movement. The main reason though is that I've never been that much of a platformer player or enthusiast. I traditionally like strategy games and action-adventure's and as a game designer am enthralled with the design of arcade and puzzle games mainly due to a history of diverse and interesting mechanics and design.

Small Simple Prototypes
As a game programmer I often find it very helpful to iron out small simple snippets to test drive certain game code such as creating a specific effect or as previous posts suggest, framework code (e.g Resource Manager, Game State Machine, and things like Timed Animation etc.). I am confident in my knowledge base for graphics and API code plus the core framework coding to move on to the more important code which is the actual interactive elements of game code. I think small self contained example code lends itself well to solving buggy mechanics or smoothing out small issues. It helps you experiment with an idea and I believe it allows you to evolve mechanics easily. At the very least it is the exercise of taking your ideas for a specific piece of code in a game and presenting you with actual syntactical and logic problems.

Once you've mastered the basic logic structure of something like, "obtaining gold coins to use in a store to buy better weapons to kill more enemies to get more gold". You can most likely implement it much better in the future. If you've never coded something before you can't predict how long it will take to get right, but once you have that and similar codes become easier and easier.

Progress

Monday, August 8, 2011

Enhancement: Smoother Movement

A while ago I found an article while searching for reasons why some of my movement code was a bit choppy and had some jitter problems. I found this article Fix Your Timestep! it's a bit abrupt but it has helped. While I wasn't able to completely fix the propagation errors since I haven't mastered the interpolation concept completely or how exactly to work it into my designs.


The Concept

Make sure that your game updates only in discrete blocks as to not cause erroneous errors in movement and to account for time missed or wait until time has accumulated.

The interpolation

Used to compensate for formula errors in translational movement which add up overtime eventually causing objects to be positioned unrealistically. Though I haven't figured out how different it makes things using interpolation versus without it.

The Code

I took the Character 8 directional-movement snippet and changed the updating thread code, check it out, not perfect but much better than it was before works pretty well at least on my PC.

// our main task for handling the rendering and for updating and
  // handling input and movement events.
  renderTask = new Thread() {
   // no local variables in a constant loop.
   long lasttime = -1;
   long msDelta  = 0;
   double dt   = 0.01;
   long time;
   double deltaTime = 0;
   double accumulator = 0;

   @Override
   public void run() {
    final Object sync = new Object();
    while (true) {
     synchronized (sync) {
      // use nano time, for more accuracy
      time = System.nanoTime();

      // record the initial last time
      if (lasttime == -1) {
       lasttime = time;
      }

      // delta in milliseconds
      msDelta = (long) ((time - lasttime) * 0.000001d);
      
      // delta in seconds
      deltaTime = (time - lasttime) * 0.000000001d;

      // cap the number of update loops needed to keep up with
      // high frame-rates.
      if (deltaTime > 0.25) {
       deltaTime = 0.25;
      }

      lasttime = time;

      accumulator += deltaTime;

      // accurate update rates, loop for each fixed 
      while (accumulator > deltaTime) {
       update(dt);
       accumulator -= dt;
      }

      //check to make sure that the renderstrategy is valid
      if(SmoothMovement.this.isShowing()) {
       render();
      }

      // if there is any time left due to quick updating and
      // rendering then the thread can sleep
      long timeLeft = (10 - msDelta);
      if (timeLeft > 0) {
       try {
        sync.wait(timeLeft);
       } catch (InterruptedException e) {
       }
      }
     }
    }

   }
  };

Give it a Try

Test it out for yourself and let me know if you still find jitter or notice any places for improvement. I try to always look for ways to improve code and will go back to code I've done previously and see a lot of new optimization's I can make.

Full Source

Here's the whole file for you to take a look at.