Articles tagged 'games'

Tic-Tac-Toe with Vue.js Jan 17 2018

Following on from our look at setting up the Vue.js and React frameworks in Rails 5.1, this post will look at building a small little component in Vue.js, strictly on the client-side (just HTML, JS and CSS, no backend Rails app!).

We're going to implement our own little version of the classic Tic-Tac-Toe game, called Tic-Tac-Vue.

Setup

We'll start by creating our structure - create a folder, an HTML page, a JS file, and a stylesheet.

mkdir tic-tac-vue
cd tic-tac-vue
touch index.html
touch app.js
touch app.css

Our HTML file is going to be very basic, let's run through our initial markup:

<html>
  <head>
    <title>Tic Tac Vue</title>
    <script src="http://unpkg.com/vue"></script>
    <link rel="stylesheet" href="./app.css" />
  </head>
  <body>
    <script src="./app.js" type="text/javascript"></script>
  </body>
</html>

This sets up our page, brings in Vue.js, our CSS and our own JS file (both currently empty).

Next up, we're going to add a root element for our app to live - this goes in our HTML body, above our script reference to our JS file:

<div id="app"></div>

And now we need the markup for our game grid itself, which we're going to put in a script tag with a special template type - this too goes in our body, and above our JS file script ref:

<script type="text/x-template" id="grid">
  <div class="grid">
    <div class="row" v-for="row in rows">
      <div class="column" v-for="column in row">
        {{column}}
      </div>
    </div>
  </div>
</script>

Let's run through that so it's clear - our grid is made up of rows and columns, fairly simple. It'll be based on a 2D array representing that data, and so we're looping through our rows data, which is an array of arrays, and then looping through each individual array, which contains the values for our individual columns. We'll be expecting three rows, and three columns for Tic-Tac-Toe of course. The column values will either be blank (unplayed), or an x or an o if someone has played in that space. That's it!

Quick note: Why do we separate the markup like this? You could include that markup within the #app div directly when the app is so simple, however there are still a couple of downsides to this. The first is that you have to then be careful which markup you're using - the browser will process that as HTML within the DOM long before Vue gets to it, meaning that if you then start to use any markup specific to how Vue works (like referencing components with tags) that isn't HTML5 compliant, the browser could end up stripping it out, or doing odd things with it that'll affect how your app works. Additionally, most apps are going to have enough complexity to have at least one component (rather than defining the app as just a root Vue app), so it makes sense to define that as such from the beginning, as it's neater and clearer to follow, as well as more cleanly allowing for expansion as the need arises. We've separated our page container markup (to host our client-side app) from the app itself, which is comprised of our specific Vue markup template, and the JavaScript we'll be writing to implement the functionality itself.

With our HTML out of the way, let's format our grid so it looks like one - here is our CSS:

.row {
  clear: both;
}
.column {
  border: 1px solid #000;
  width: 50px;
  height: 35px;
  float: left;
  text-align: center;
  padding-top: 15px;
}

Fairly simple - we're floating each column and having them on the same row, with each row clearing the float to move on to the next row - we setup a border for the grid itself, set a specific height and width, and make sure that if a text value is shown (x or o) it looks nice within the box for that space on the game grid.

Now we get to the fun part - our JavaScript! We have two tasks now to get our initial grid functional - we have to create and define our grid as a component, and we then have to create our Vue app with our grid component being rendered into our #app empty div above.

First, we define our grid component:

var Grid = Vue.component('grid', {
  template: '#grid'
});

This is easy enough to follow - we define a component, named Grid, using the template with id grid, and store it as Grid for reference elsewhere.

We're going to need to set our initial data for the component though - the rows and columns that the template renders:

data: function() {
    return {
      rows: [
        ['', '', ''],
        ['', '', ''],
        ['', '', '']
      ]
    };
  }

As we described above for our template, rows is a 2D array, an array of arrays, each array representing a row, and each value within representing a column in that row - and we're starting with them all blank and unplayed.

Next up, let's define our App:

var App = new Vue({
  el: '#app',
  components: {
    Grid
  },
  render: function(createElement) {
    return createElement(Grid);
  }
});

Here we specify the element our app will reside in, so #app, and then the components we need to use - we just have one, our Grid component. Lastly, as we don't have any markup within our #app element to tell our app how and what to render, we do it with a custom render method, referencing our game grid component.

Open up index.html now in a browser and you'll see our grid! That's great, but it doesn't do a lot. Let's start making it so we can play the spaces, and get a game going!

Game on

First of all, we want the user to be able to select a space, and if it's empty, it'll place an x or an o in it, depending upon whose turn it is. Let's add a variable to our data to determine whose go it is, and which one is next:

  data: function() {
    return {
      rows: [
        ['', '', ''],
        ['', '', ''],
        ['', '', '']
      ],
      next: 'x'
    };
  },

So we've just added that next attribute with a default value of x to our component data.

Now we'll add a method to our component definition, that'll handle when a space is tapped:

methods: {
  tap: function(e) {
    if (e.target.innerText == '') {
      e.target.innerText = this.next;
      this.next = (this.next == 'x' ? 'o' : 'x');
    }
  }
}

So it only works if the space is free, sets the space, and then toggles which player is next. Reload our page, tap a space, and… nothing. That's because we have one final step - we need to hook up tapping on the game grid spaces to that method we just wrote!

Change the markup in our #grid component template, so it's like this:

<div class="column" v-for="column in row" v-on:click="tap">
  {{column}}
</div>

The v-on:click attribute is added, which adds an event handler for the click event to the component, pointing at the tap method. Try reloading again now, et voila! Tapping on spaces sets the next player character in the space, with no overwriting involved. Pretty neat, and not a lot of code either!

Data binding

We have our action in place, but we can't do much with spaces played as we're not updating our data based on the user input - we're just directly updating the UI. Instead, let's update our data array, and have that reflect to the UI, which is a much better way to handle things.

In our markup, we'll alter our Grid component template as follows:

<script type="text/x-template" id="grid">
  <div class="grid">
    <div class="row" v-for="(row, row_index) in rows">
      <div class="column" v-for="(column, column_index) in row" v-on:click="tap" v-bind:data-row="row_index" v-bind:data-column="column_index">
        {{column}}
      </div>
    </div>
  </div>
</script>

Specifically, we're keeping track of the row and column index in our two-dimensional array looping, and then binding it as row and column data variables to reference when a space is played.

Over to our tap method now:

tap: function(e) {
  if (e.target.innerText == '') {
    let rows = this.rows;
    rows[e.target.attributes['data-row'].value][e.target.attributes['data-column'].value] = this.next;
    this.rows = rows.slice(0);
    this.next = (this.next == 'x' ? 'o' : 'x');
  }
}

Here we are grabbing the data rows, then updating the value of our column, using the data-row and data-column bindings present on the element tapped to find the right location in the array. The reason we do a slice(0) is because the way that the data binding works in Vue, the rows property in our data is reactive (meaning it is being watched for changes), but individual changes within the array won't trigger that watcher, and therefore a re-render. Instead we're using a local rows variable, and then effectively copying the whole array back into our data, which triggers our reaction and an update of the UI using our template (rather than us modifying the element directly).

So now we have our app data binding correctly, we can implement logic based on the current rows/columns data to check for win criteria!

Winner winner

To check for a winner, we need to check each row for three matching values, each column for three matching values, and the two diagonal paths for three matching values. If any of those are true, we have a winner! We can boil this down to the following:

checkWinner: function() {
  return (
    this.checkValues(this.rows[0]) ||
    this.checkValues(this.rows[1]) ||
    this.checkValues(this.rows[2]) ||
    this.checkValues([this.rows[0][0], this.rows[1][0], this.rows[2][0]]) ||
    this.checkValues([this.rows[0][1], this.rows[1][1], this.rows[2][1]]) ||
    this.checkValues([this.rows[0][2], this.rows[1][2], this.rows[2][2]]) ||
    this.checkValues([this.rows[0][0], this.rows[1][1], this.rows[2][2]]) ||
    this.checkValues([this.rows[0][2], this.rows[1][1], this.rows[2][0]]));
},
checkValues: function(values) {
  return (values[0] != '' && values[1] != '' && values[2] != '' && (values[0] === values[1]) && (values[1] === values[2]));
}

Add those methods to our component underneath our tap definition.

checkValues is easy enough - we expect an array of three values, we make sure all the values are not blank, and that they are the same. checkWinner then calls checkValues for all of our different possible value arrays - firstly checking each of the three rows (each of those is already an array with three elements), then the three columns, and then lastly, the two diagonals.

With our win logic in place, we now just need to hook it up. We'll add an additional property, finished, to our data:

data: function() {
  return {
    rows: [
      ['', '', ''],
      ['', '', ''],
      ['', '', '']
    ],
    next: 'x',
    finished: false
  };
},

We'll then reference that to make sure that you can't keep playing once we're finished - at the top of the tap method:

tap: function(e) {
  if (!this.finished && e.target.innerText == '') {

Lastly in terms of logic, we just need to amend the end of our tap method:

if (this.checkWinner()) {
  this.finished = true;
} else {
  this.next = (this.next == 'x' ? 'o' : 'x');
}

So we only go to our next player if we've not found a winner - if we have found a winner, we mark the game as finished.

Playing the game now, we'll see that if one player wins, you can't continue playing. But some kind of feedback to indicate that the game is over (and to say who has won!) would be good. Easy! We can do that with a single additional line in our template - at the bottom within our .grid div, below the rows & columns, add:

<div class="status" v-if="finished">Finished! {{next}} wins!</div>

Add the following to app.css to make sure our message sits underneath the grid:

.status {
  clear: both;
}

Now when someone wins and the game stops, you can see who has won!

Whose turn?

It'd be good if we could show whose turn it is next, in case the players lose track. We already have the data, and as you can probably guess from how we displayed the finish state, it's fairly easy to do - change our .status text div to the following:

<div class="status">
  <span v-if="finished">Finished! {{next}} wins!</span>
  <span v-else>Next go: {{next}}</span>
</div>

Here we're making use of v-if and v-else to make sure only one shows - either we're showing whose turn is next, or we show the finished/winner text as the game is over!

Replayability

We're almost there with our incredible take on Tic-Tac-Toe, but rather than having to refresh the page after every game to go again, wouldn't it be cool if we could just press a restart button instead?

We're going to move our code to determine the next player into its own method, so we can re-use it:

nextPlayer: function() {
  this.next = (this.next == 'x' ? 'o' : 'x');
},

Next up, we'll change the tap method to use that function - so for the winner check or next player logic:

if (this.checkWinner()) {
  this.finished = true;
} else {
  this.nextPlayer();
}

Now, we'll implement our restart method:

restart: function(e) {
  this.rows = [
    ['', '', ''],
    ['', '', ''],
    ['', '', '']
  ];
  this.finished = false;
  this.nextPlayer();
},

This just resets the rows, makes sure the game is no longer finished, and triggers the next player. Let's hook that up to something in the template now:

<div class="status">
  <div v-if="finished">
    <p>Finished! {{next}} wins!</p>
    <a v-on:click="restart">Restart</a>
  </div>
  <span v-else>Next go: {{next}}</span>
</div>

Our restart link shows when the game is finished, and tapping it resets our game accordingly!

Stalemate

The last thing we need to handle, is the dreaded stalemate. When two world class Tic-Tac-Toe players are locked together, head-to-head, neither willing to budge an inch, we could end up with a tied game. What then? The game doesn't get marked as finished, but there are no more moves to make. The games cannot continue!

So let's add a check for that state, and handle it accordingly. We'll start by refactoring our checkValues method, so we can re-use the blank check within it for checking if all the spaces have been played:

checkValues: function(values) {
  return this.checkValuesPresent(values) && this.checkValuesMatch(values);
},
checkValuesPresent: function(values) {
  return (values[0] != '' && values[1] != '' && values[2] != '');
},
checkValuesMatch: function(values) {
  return (values[0] === values[1]) && (values[1] === values[2]);
}

As you can see, we've separated the two parts of checkValues into a check for whether the specified values are all present (checkValuesPresent) and whether the values all match (checkValuesMatch). Now we can use the checkValuesPresent check in our stalemate logic - add the following method:

checkStalemate: function() {
  return !this.finished &&
    (this.checkValuesPresent(this.rows[0]) &&
    this.checkValuesPresent(this.rows[1]) &&
    this.checkValuesPresent(this.rows[2]));
},

We're just making sure that the game isn't yet finished, and all of the rows have values present - then it must be a stalemate!

We'll add an additional property to track stalemates, so add the default value to our data:

next: 'x',
finished: false,
stalemate: false

And then we'll update our tap method to account for checking for stalemates:

if (this.checkWinner()) {
  this.finished = true;
} else if (this.checkStalemate()) {
  this.stalemate = true;
  this.finished = true;
} else {
  this.nextPlayer();
}

We have to check for a stalemate after a win, because there might be a winner on the very last space! If it is a stalemate, the game is finished, but also the stalemate property is set.

We can now use this on the frontend within the template to display something more relevant:

<div class="status">
  <div v-if="finished">
    <p v-if="stalemate">It's a draw! Stalemate!</p>
    <p v-else>Finished! {{next}} wins!</p>
    <a v-on:click="restart">Restart</a>
  </div>
  <span v-else>Next go: {{next}}</span>
</div>

We're either displaying our draw statement for stalemates, or the winner text, and the restart button either way so a new game can start. The only thing left, is to make sure our stalemate flag is reset on restart:

restart: function(e) {
  this.rows = [
    ['', '', ''],
    ['', '', ''],
    ['', '', '']
  ];
  this.finished = false;
  this.stalemate = false;
  this.nextPlayer();
},

Now win, lose or draw, players can continue playing!

Wrapping up

So our Tic-Tac-Toe game is pretty much feature complete, at least in terms of a basic game two people can play on a single computer. It saves on paper and drawing, and we've done it all with just a little HTML + CSS, and a bit of fun JavaScript, all thanks to Vue.js!

This is one way to build a Vue.js app, and is better suited for small, self-contained ideas like this. In a future post we'll look at going back to Webpack and using vue-cli, so that we can do things like having each component of our app (template HTML and JS logic) in its own .vue file, that's pre-processed accordingly to build the JS for our page. As projects increase in complexity, that helps to give them structure and keep them maintainable.

Check out the full code for this post at https://github.com/ejdraper/tic-tac-vue, and you can play the game itself below!

Comments

7DFPS: after the dust has settled Aug 21 2013

I thought it might be fun to look back at 7DFPS, now the dust has settled, to look at what went right and what went wrong with the development of Velocity Ball. The observations here might be useful to other people doing a time limited game jam in future, and certainly will help me to solidify my experiences to make next time around even better.

Positives

  • I learned a lot about Unity, in a very short space of time
  • I shipped a pretty much complete (if a bit buggy, and short on content) game concept in 7 days
  • 7DFPS gives you a spotlight, so a bunch of people have played the web and Mac versions!
  • I think the game idea has a lot of potential, and I'm left with a lot of ideas on how to improve it and extend it

Improvements

  • I probably spent too long on a few bells and whistles, then had a few late breaking physics related bugs that were tough to fully nail down and fix before the end - specifically I was messing around with the image effects to create the splash screen loading into the main game, as well as alternate camera angles for the main menu, and the goal line cams that show on big screens above each endzone
  • The game doesn't have much longevity, and isn't particularly taxing as the AI is fairly one directional at the minute

On the whole though, it was an enjoyable exercise, and Velocity Ball is definitely something I'd like to continue working on and building out from the prototype it is now, into something bigger and better.

Other entries

Another bonus of course was being able to take a look at what other people can get done in a week - and it's pretty humbling and awesome to see some of the amazing things people did. Some clearly focused on short, polished experiences, while others worked on a broader, more ambitious vision that had a little less polish, but there were some gems in there. Here are a few of my favourites, after having gone through just a small percentage of the 167 games that were finished.

Escape Sequence

Really polished, with the design fitting the concept perfectly - the idea is to escape each maze, with them seemingly getting tougher and tougher, on your way to the escape pods

Root Arena

Old school style fragfest, frenetic multiplayer action - a great variety of weaponry, and it even has it's own stats server and leaderboards, great for bragging rights!

Hunting Anubis

Nice change of pace, aerial combat - good style, was hard to test the actual combat as no one else was on when I tested it, but I can imagine it'd be quite tense!

Beyond Perception

Another innovative idea, this time a puzzle game set inside museum paintings, 2D puzzles in 3D - nice to see such an abstract, fresh concept squeezed into a game produced in such a short space of time

Superhot

Incredibly stylish aesthetic, with a great unique selling point in that time only moves forward in the game when you're moving - would make a fantastic full game

Unity

Worth noting that all of the above games were built using Unity, just like Velocity Ball - I'm not sure exactly, but it seems like a good majority of the games were built with Unity, quite possibly as it lends itself so well to being able to get something up and running in a very short space of time, without really having to sacrifice the quality in visuals or fidelity that you might have expected from such a rapid tool in the past.

Over to you

How about your 7DFPS? What was good, what was bad, and what would you do differently next time? And what are your favourite games from the jam? Sound off in the comments, or hit me up on Twitter.

Comments

Velocity Ball (#7DFPS): Dev Blog #3 Aug 19 2013

7DFPS finished a couple of days ago, and I just about wrapped up a version of Velocity Ball I was happy with. It then took me a few days to sort out a video demoing the game, and also a downloadable build.

You can check out more about the game, including the Mac download and controls (keyboard/mouse or PS3 controller) for the game here - Windows build hopefully coming soon, and I might well get a build running on the OUYA too.

I'm pretty pleased that in a week I was able to get 2v2 gameplay with (fairly limited) AI in, working arena, some nice special effects, and proper game timers, scoring and restarting. I had a fair few other ideas beyond that, but 7 days isn't quite as long as it seems! Below is the video run through of what the build looks like right now:

Check out the video - and if you're on Mac, download it and give it a go yourself! You can let me know what you think in the comments on this post, on Twitter, or via the contact form. I'm going to pause and take stock of the project a little bit now (and work on some other prototype ideas), but then will return to it and do another iteration on the idea with feedback and refinements as I think it could be a lot of fun with a bit more time and attention!

If you've worked on a 7DFPS game too then please let me know - I'm checking out lots on the 7DFPS site, but don't hesitate to shout about your game so I know to take a look!

UPDATE: After a few requests, I've added the ability to play the game in browser using the Unity web player. Click here to check it out!

Comments

Velocity Ball (#7DFPS): Dev Blog #2 Aug 14 2013

Just a couple of days to go now for 7DFPS, and since the last dev blog I've only really had time to improve the arena and presentation a bit, and add a menu.

Here are a few screenshots of those features:

  • Main menu
  • Bot gameplay
  • Bot opposition score
  • Home endzone
  • Player score

So I'm refining the list of what is possible in the remaining couple of days (multiplayer is definitely a post-7DFPS task now), and I'm hoping to get the following done:

  • Sound effects (the game is entirely silent right now!)
  • Countdown restart and position reset after each goal to make it more tactical and less hectic
  • Improved AI and ball physics
  • Getting games going solely with AI, for a spectator mode, and/or to make the main menu more interesting
  • Improving the UI and presentation a bit further
  • Dual stick controller support

The next dev blog will definitely be a video run through, most likely fairly close to the end of 7DFPS in the hope that I can show off as many of the above features in there as possible! I'll then probably package the game up for download on Mac (Windows too if I get time to test it).

A few more resources that have been useful in the last couple of days:

  • NGUI: Next-Gen UI, great asset for building user interfaces on top of and around your games
  • NGUI Basic Menu, a cracking little tutorial video on putting together a basic menu using NGUI

Hope everyone else is making good progress, looking forward to spending some time looking at other entries over the weekend!

Comments

Velocity Ball (#7DFPS): Dev Blog #1 Aug 12 2013

3 days into my 7DFPS game, and things are progressing quite well. I've learned the basics of blocking out arenas in Blender, and setting them up properly with UV unwrapping for texturing them in Unity.

I've got working ball physics (with a few quirks to be ironed out), and possession/throwing of the ball working. I've also implemented the two endzones, and got some basic AI working with teammates and opposition players in a 2v2, as well as scoring (red vs blue teams, naturally). The bot characters even have swish idle to run animation transitions after messing around with Mecanim.

I'm planning on doing a proper run through video once I add a few more bits in, namely I need to add a time limit to games, and also fine tune some of the mechanics for scoring, resetting after a score, and some of the ball physics and AI edge cases. At the minute it's only working on keyboard/mouse too, so I'd like to really getting it working on a controller (either OUYA or PS3 I think). After that, I can start to flesh out the content a bit more, sound effects, special effects, different arenas and team selection, and if I can improve the AI, it might be playable with more than just 2v2 as well (right now the bots go a little loopy if there are too many!). And who knows, maybe I'll get on to some multiplayer even which would be really great!

Some of the great tutorials and resources I've used so far:

Hoping to make enough progress now for a post like this each day until 7DFPS is up, but we'll see - how is everyone else getting on?

Comments

Upcoming Jam: #7DFPS Aug 8 2013

On Saturday, this years 7DFPS game jam begins!

It's a fairly straightforward jam - the only rules seem to be that you have 7 days to make a first person game. It's a great idea, as the first person genre is perhaps the most stale, certainly at AAA title level, so having hundreds, maybe even thousands of people getting together to work on crazy and fresh ideas for the genre sounds like fun.

I'll be taking part, and while not buttoned down 100% yet, I think I'm going to try to switch the S in FPS from "shooter" to "sports" - think something like the futuristic football and zero-G ball games you see in sci-fi movies, or Grifball from Halo but without any weapons. Perhaps even like a first person Speedball, for those that remember that far back.

Of course, how much I'm able to get done in seven days remains to be seen, but trying it and seeing is a part of the fun, and knowing that lots of like minded individuals are all trying to do something that isn't a Michael Bay-esque set piece driven FPS set in a modern theatre of war is going to be very refreshing.

Who else is joining in the fun? What ideas do y'all have? Get in touch and let me know on Twitter! I'll be posting up some regular progress updates throughout the week, and I look forward to playing some of the games you are going to be creating!

Comments

All new KickCode and introducing Hijax Games Jul 6 2013

I have completely forgotten to post on here about the relaunch of the KickCode site, or my new gaming focused development brand, d'oh!

First things first, I rebuilt and relaunched the KickCode site a good few weeks ago. The new site is smarter, cleaner and clearer, and focuses on what we do best - helping businesses improve. Whether it’s increasing revenue or users, decreasing churn, or another business goal you have, we can almost certainly help you achieve it. We’re providing lots of good business and technical content on the blog too, and you can sign up for the newsletter to get a nice round-up of things we’ve written, worked on, or found interesting. Check out the new site now!

I’ve also launched a separate gaming focused brand, called Hijax Games. The thinking behind this was to provide the same great focus we do on KickCode, but specific to the game development niche. We’ll be working on our own games and sharing development progress from those, as well as being available to work on client game development. Lastly, we’ll be providing great game development content to help other game developers, with the tools we’re best at, from RubyMotion and Joybox, to Unity3D. Expect lots of interesting stuff over the coming months, and again you can sign up to the Hijax newsletter to be kept in the loop.

So that’s that, busy few weeks launching those - let me know if you have thoughts, comments or questions on the new sites, and please get in touch if you think KickCode or Hijax Games can help you with your idea!

Comments

Page 1 of 1 |