Click here to Skip to main content
Click here to Skip to main content

Tagged as

Go to top

Hot Shots

, 6 Nov 2013
Rate this:
Please Sign up or sign in to vote.
A cross between Geometry Wars, Asteroids and Micromachines

Please note

This article is an entry in our AppInnovation Contest. Articles in this sub-section are not required to be full articles so care should be taken when voting.

Platform

Lenovo 27" All in One

Category

Games

What is it?

Hot Shots is a multiplayer action game for 2–10 players targeted towards the 27″ Lenovo All-In-One.

Inspirations

I am drawing on some of my favourite game features and mechanics, and combining them to form something new which is naturally suited to the large, multi-user touch form factor. In particular this game takes inspiration from:

  • Micromachines — This was the classic bird's eye view multiplayer racer. The game featured very limited input controls (accelerate and turn) and much of the fun came from directly competing with friends on the same screen as well as the high speed collisions in a sometimes perilous environment. I will be trying to capture this fun dynamic in having multiple users race each other on the same screen with a bird's eye view.
  • Asteroids / Space War — Also classic games. The most appealing element for me was the method of control. Similar to Micromachines in that it is restricted to turning left, right and accelerating, only your inertia was maintained when you turned. This simple use of physics gives a very satisfying "feel" to the controls. I will be using a similar means of control in this game, so that you thrust in a given direction which modifies your momentum, rather than strictly sending you in that direction.
  • Geometry Wars — A modern classic, with a distinctive visual style. Geometry Wars is an intense survival shoot 'em up which has inspired many other games. I love the shader post-processing which is applied to the visuals in this game, and will be giving the graphics in my game a similar treatment.
  • Supercars II & Mario Kart — These racers, and countless others have wonderful methods of sabotaging the other players through various weapons and power-ups. In intend to add a healthy dose of chaos into the racing action through similar mechanics.

Overview

The game combines the controls of Asteroids with the frantic multiplayer fun of Micromachines and the form factor of a large tablet to offer a novel fusion. Two or more players compete against each other to either out race or out gun their way to victory.

The basic game modes are Deathmatch and Sprint. Deathmatch is a straight up battle, similar to Space War, where players struggle both against each other and the force of gravity, as one or more suns pulls them toward a toasty demise. Sprint is a frantic dash around a circuit, with shortcuts, an interactive environment, and the hilarity of colliding with a large number of other players in a confined space.

Additional game modes will be added as time permits:

  • Football
  • Capture the Flag
  • Team Deathmatch
  • Team Sprint

Intended Uses

  • Head to Head — Players can sit either end, stare each other down and play a conventional two player game.
  • Party Play — The game should work equally well with a larger number of people (theoretically we could have something crazy like 14 players if gamepads are also used) playing simultaneously, casually joining games midway through. Deathmatch games don't penalise late entrants. Whilst races tend to have players starting and finishing together, why not just turn up halfway through and get in everyone's way for the fun of it? I think a fairly large number of simultaneous users is possible if people essentially stand side on, looking & reaching inward. Control requires access to the edge of the screen with just one finger. The real limit will depend on the size of the players rather than the capabilities of the device.

Development Approach & Technical Details

Taking a few lessons from last year's Celerity entry, I'm focusing on creating something which the basic mechanics and world can be created very quickly leaving much more time for polish. I'm keeping it strictly 2D for simplicity, and this time rather than coding everything myself from scratch, I'll be plugging in a couple of proven libraries to handle some of the trickier aspects. Although I'm using frameworks, the app is still from scratch in that it hasn't been started before the competition.

The development process will look something like the following:

  • Create project, get it working in Windows 8 & VS2012 (Done)
  • Create input wrappers to consolidate and translate various input sources into meaningful game data (Done)
  • Display rough / temporary sprites (Done)
  • Hooking sprites to physics engine (Done)
  • Refine controls / "feel" / physics settings (Done)
  • Create "track" elements
  • Create levels
  • Implement game logic, e.g. lives, end conditions etc.
  • Gameplay tweaking
  • Create proper sprites & other graphics
  • Particle effects (Part Done)
  • Add ridiculous shader effects here (the chances of me being patient enough to genuinely leave this until now are low, but we'll see!) (Done!)
  • Create an attractive background (pseudo-3D starfield + nebular effects)
  • Top secret surprise background technique for the Deathmatch level (!)
  • Polish & Final Testing
  • Package / Create installer
  • Realise I've forgotten something and repeat

How the input will support many players

My concept for an input system is that dotted around the edge of the screen are a number of player "zones". These will be player-specific HUDs, which will also in the case of touch-controlled players double up as touch input panels.

Currently a player can join the game by tapping a zone to activate it (obviously this is not indicative of the final graphical look/feel):

This will shortly be reworked so that an area is not specific to a method of input (keyboard/gamepad/touch), and could feasibly be activated by anything. It would then only show the touch input panels if it was activated by touch. There will be score & other info specific to the player located here in order to ensure it is oriented to their angle of vision.

Languages

  • C#
  • HLSL

Frameworks

Why is this perfect for the Lenovo All-In-One?

  • Play from any angle — Touch input control zones are oriented around each edge of the device allowing a player to play equally well from any side.
  • Carefully designed around 10-touch point limit — The game's input has been designed based around the maximum number of touch points. Players can control their ships with a single finger. Different game modes offer further controls for fewer player counts, e.g. 5 players or less can use multi-touch firing controls as well.
  • Making use of the large size — The physical dimensions are necessary to support a larger number of players than about 4 people. The 27″ area has a much larger circumference allowing even more simultaneous players.
  • Power-hungry visual effects — The graphics in this game will lend itself to post-processing and a powerful GPU. The AIO offers a generous amount of power in terms of CPU & GPU, and this game will savage them both!
  • Viewing-angle-independent presentation — The game does not depend on viewing it from a particular orientation.
  • Casual drop in & drop out — smaller tablets naturally fit 1–2 player games where the device is passed around the players. For a larger, static device I envisage a transient crowd of players moving about the device, dropping in and out of games. This game will work for as few as 2 players, but would handle a party or youth club context equally well.
  • No occlusion issues — Occlusion has been designed out of the touch interface. The touch input panels are located round the edges so that players' hands and fingers won't disrupt input or vision of other players.
  • Striker integration — For Gravity Well Survival game mode. Place a striker down to spawn 1–2 gravity wells mid-game.
  • Joystick integration — For 2 player non-combat game modes, dual sticks could be used for independent control of rotation and thrust.

Project Viability

I believe this project is viable both in terms of time scale and my abilities. The basic mechanics and display of the game have been deliberately limited in order that I might get the game up and running as quickly as possible, leaving a larger percentage of time in the competition for testing, tweaking and enhancing the game. I'll get the basic game up and running and add different game modes according to remaining time.

That's not to say there won't be challenges. I don't have much experience writing complex shaders and particle effects, and they will feature heavily in creating the look and feel of this game and its visual effects. I do have some excellent reference materials, however.

Last year I just managed to get Celerity done in about six weeks after a late start:

This year I have almost twice as much time, a year's more experience, and the game is only 2D. I have high hopes for this year's entry, and win or lose, I hope to make a cracking game people will want to play over and over.

Dev Diary - Progress

I've been mainly concentrating on code relating to input. Whilst it sounds easy to read keyboard, touch and GamePad input, especially given the XNA framework, I've "gone to town" on expanding the API XNA offers. A typical problem in XNA is for example the idea of pressing Escape to quit. If you're in-game you'd want it to "go back" to the main menu rather than quit, but because XNA just exposes a key down, and not a key up as such, you manually have to track it. Rather than have horrible input-processing code littered through my project I wrote these two general use helper classes so that the caller can understand the difference between continuously pressed keys, and keys which are newly pressed this update cycle.

You might find these two input wrapper classes handy in your project for finding keys and buttons which have been pushed as a once-off occurrence, for example to fire a missile, or execute a command.

KeyboardService

/// <summary>
/// A wrapper for the raw keyboard input which allows key change tracking
/// </summary>
public class KeyboardService : GameComponent
{
    /// <summary>
    /// A flag indicating whether the keyboard state is the same
    /// as before, used limit the average amount of polling
    /// </summary>
     private bool isLastKeyboardStateSame = false;

    /// <summary>
    /// The state of the kyeboard in the previous Update
    /// </summary>
    private KeyboardState lastKeyboardState;

    /// <summary>
    /// A List of enumerated key values
    /// </summary>
    private List<Keys> keys;

    /// <summary>
    /// A list of currently pressed keys
    /// </summary>
    private IEnumerable<Keys> knownKeyPresses;

    /// <summary>
    /// A list of pressed keys which are newly pressed this update
    /// </summary>
    private IEnumerable<Keys> newKeyPresses;

    /// <summary>
    /// Constructor for the KeyboardService class
    /// </summary>
    /// <param name="game">The parent game</param>
    public KeyboardService(Game game)
        : base(game)
    {
        knownKeyPresses = new List<Keys>();
        newKeyPresses = new List<Keys>();
        keys = Enum.GetValues(typeof(Keys)).Cast<Keys>().ToList();
    }

    /// <summary>
    /// A list of pressed keys which are newly pressed this update
    /// </summary>
    public IEnumerable<Keys> NewKeyPresses { get { return newKeyPresses; } }

    /// <summary>
    /// A cached copy of the Keyboard State
    /// </summary>
    public KeyboardState KeyboardState { get { return lastKeyboardState; } }

    /// <summary>
    /// Updates the class to determine changes in key presses
    /// </summary>
    /// <param name="gameTime">The current game time</param>
    public override void Update(GameTime gameTime)
    {
        var newKeyboardState = Keyboard.GetState();

        if (lastKeyboardState == newKeyboardState)
        {
            if (isLastKeyboardStateSame)
            {
                return;
            }
            else
            {
                isLastKeyboardStateSame = true;
            }
        }
        else
        {
            isLastKeyboardStateSame = false;
        }

        lastKeyboardState = newKeyboardState;
        var keyDowns = lastKeyboardState.GetPressedKeys();
        var keyUps = keys.Except(keyDowns);

        // ToList() necessary for performance with multiple keydowns
        newKeyPresses = keyDowns.Except(knownKeyPresses).ToList();
        knownKeyPresses = knownKeyPresses.Concat(newKeyPresses).Except(keyUps).ToList();

        base.Update(gameTime);
    }
}  

GamePadService

    /// <summary>
/// A wrapper for the raw GamePad input which allows button press change tracking
/// </summary>
public class GamePadService : GameComponent
{
    /// <summary>
    /// A flag indicating whether the GamePad state is the same
    /// as before, used limit the average amount of polling
    /// </summary>
    private List<bool> isLastGamePadStateSame = new List<bool> { false, false, false, false };

    /// <summary>
    /// The state of the GamePad in the previous Update
    /// </summary>
    private List<GamePadState> lastGamePadState;

    /// <summary>
    /// A List of enumerated button values
    /// </summary>
    private List<Buttons> buttons;

    /// <summary>
    /// A list of currently pressed buttons for each gamepad
    /// </summary>
    private List<IEnumerable<Buttons>> knownButtonPresses;

    /// <summary>
    /// A list of pressed GamePadButtons which are newly pressed this update for each gamepad
    /// </summary>
    private List<IEnumerable<Buttons>> newButtonPresses;

    /// <summary>
    /// Constructor for the GamePadService class
    /// </summary>
    /// <param name="game">The parent game</param>
    public GamePadService(Game game)
        : base(game)
    {
        lastGamePadState = new List<GamePadState> { GamePad.GetState(PlayerIndex.One), 
          GamePad.GetState(PlayerIndex.Two), 
          GamePad.GetState(PlayerIndex.Three), GamePad.GetState(PlayerIndex.Four) };

        knownButtonPresses = new List<IEnumerable<Buttons>> { 
          new List<Buttons>(), new List<Buttons>(), new List<Buttons>(), new List<Buttons>() };

        newButtonPresses = new List<IEnumerable<Buttons>> { new List<Buttons>(), 
          new List<Buttons>(), new List<Buttons>(), new List<Buttons>() };

        buttons = Enum.GetValues(typeof(Buttons)).Cast<Buttons>().ToList();
    }

    /// <summary>
    /// A list of pressed GamePadButtons which are newly pressed this update
    /// </summary>
    public List<IEnumerable<Buttons>> NewButtonPresses { get { return newButtonPresses; } }

    /// <summary>
    /// A cached copy of the GamePad State
    /// </summary>
    public List<GamePadState> GamePadState { get { return lastGamePadState; } }

    /// <summary>
    /// Updates the class to determine changes in key presses
    /// </summary>
    /// <param name="gameTime">The current game time</param>
    public override void Update(GameTime gameTime)
    {
        foreach (var playerIndex in Enum.GetValues(typeof(PlayerIndex)).Cast<PlayerIndex>())
        {
            int index = (int)playerIndex;
            var newGamePadState = GamePad.GetState(playerIndex);

            if (lastGamePadState[index] == newGamePadState)
            {
                if (isLastGamePadStateSame[index])
                {
                    return;
                }
                else
                {
                    isLastGamePadStateSame[index] = true;
                }
            }
            else
            {
                isLastGamePadStateSame[index] = false;
            }

            lastGamePadState[index] = newGamePadState;

            var keyDowns = Enum.GetValues(typeof(Buttons)).Cast<Buttons>().Where(λ => 
                                lastGamePadState[index].IsButtonDown(λ)).ToList();

            var keyUps = this.buttons.Except(keyDowns);

            // ToList() necessary for performance with multiple keydowns
            newButtonPresses[index] = keyDowns.Except(knownButtonPresses[index]).ToList();
            knownButtonPresses[index] = 
              knownButtonPresses[index].Concat(newButtonPresses[index]).Except(keyUps).ToList();
        }

        base.Update(gameTime);
    }
}

As you can see by the following screenshot, the basic post-processing is done (I couldn't wait, so had to do it early) and the engine thrust particles are done.

You really need to try it to get the effect, so download the demo to get a feel of where this is headed.

The basic idea of the control mechanism has changed from the original plan based on trial and error with it. Originally I was going very much for the classic Asteroids method of input - i.e. a Thrust control and a Rotation control. I've played many games with a similar control, with keys, gamepads and joysticks but it turns out it really sucked for touch!

Now there is an approach I've called the Flight Computer. The idea is that you drag in the direction you want to go, and the ship works out the necessary adjustments in angle to make it happen. It sounds like it might be too easy to control, but I'm really pleased with the outcome. Again, the demo demonstrates it better than I'll be able to describe, but this diagram might help:

History

  • First draft
  • Added dev diary update

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Adam David Hill
Software Developer
United Kingdom United Kingdom

Bio:

Musician turned Software Engineer (turned professional around 6 years ago). Mainly interested in games & mobility.

Sometimes I do real work, too.

 

My articles:

 

My open source software:

Follow on   Twitter   Google+   LinkedIn

Comments and Discussions

 
QuestionEven bigger congratulations from me. PinprotectorPete O'Hanlon14-Jan-14 6:19 
AnswerRe: Even bigger congratulations from me. PinprofessionalAdam David Hill14-Jan-14 11:17 
QuestionCongratulations on reaching the final round. PinprotectorPete O'Hanlon10-Dec-13 6:38 
AnswerRe: Congratulations on reaching the final round. PinprofessionalAdam David Hill10-Dec-13 8:17 
QuestionHow's app development going? Will you be submitting on time? PinstaffKevin Priddle24-Oct-13 4:55 
AnswerRe: How's app development going? Will you be submitting on time? PinprofessionalAdam David Hill24-Oct-13 11:27 
GeneralRe: How's app development going? Will you be submitting on time? PinstaffKevin Priddle25-Oct-13 10:49 
QuestionPlayed the demo a little. Few question PingroupGrasshopper.iics21-Sep-13 3:15 
AnswerRe: Played the demo a little. Few question PinprofessionalAdam David Hill21-Sep-13 3:21 
GeneralRe: Played the demo a little. Few question PingroupGrasshopper.iics21-Sep-13 3:45 
GeneralRe: Played the demo a little. Few question [modified] PinprofessionalAdam David Hill21-Sep-13 4:01 
QuestionSource code files are missing PinmemberTridip Bhattacharjee2-Sep-13 20:49 
AnswerRe: Source code files are missing PinprofessionalAdam David Hill2-Sep-13 21:01 
AnswerRe: Source code files are missing PinprofessionalAdam David Hill2-Sep-13 22:12 
GeneralMy vote of 5 PinprofessionalWilson PS29-Aug-13 9:19 
GeneralThanks! PinstaffKevin Priddle21-Aug-13 9:34 
GeneralRe: Thanks! PinprofessionalAdam David Hill21-Aug-13 10:20 
GeneralRe: Thanks! PinstaffKevin Priddle22-Aug-13 8:14 
GeneralMy vote of 5 PingroupGrasshopper.iics17-Aug-13 9:48 
GeneralRe: My vote of 5 PinprofessionalAdam David Hill19-Aug-13 2:27 
GeneralMy vote of 5 PinprofessionalAbhishek Nandy17-Aug-13 8:40 
GeneralRe: My vote of 5 PinprofessionalAdam David Hill19-Aug-13 2:17 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140916.1 | Last Updated 6 Nov 2013
Article Copyright 2013 by Adam David Hill
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid