Capstone: The Aftermath

I kinda disappeared after the last post. I meant to post more, I did, but it all got so hectic. Honestly, I wasn’t even aware we had to do a postmortem, so here I am, late as all hell, but hopefully it counts for something, and to anyone reading that might be curious, it gives some closure to the semester and what we’ll see going into the next.

I don’t quite know if there’s anything particularly expected out of it, so I’ll just cover what I know from my end.

First and foremost, our game went through. There were 23 teams, 18 of which presented, 16 of which demo’d (demo night was where teams that chose to show off their games actually showed them off, presentation night was just a final pitch to the faculty and other students; 2 of the teams that presented wished to show their progress but not attempt to go through), and 11 of which went through.

If you’re tracking other blogs, the 11 that went through are:

  • Breakout Brew
  • Dawn of the Celestialpod
  • Forsaken Fish Fighters
  • Frog Snatchers
  • Keeper
  • Lucha Megadrive
  • Nautical Nonsense
  • Protocol Aurora
  • Re[mod]
  • Sojourn (That’s us!)
  • Toybox Shooter

We were incredibly happy to have made it through. A premature thanks to my entire team: Matt Makuch, Tucker Cole, Mike Andreula, and Ren Golis who were all so instrumental towards making this game to the state it is.

The following week was then scrambling to find people for our team. I won’t sugarcoat this post, it’s been me just free flowing my thoughts, and I’m going to keep it that way. We had a really tough time finding people who wanted to work on our game.

We had a lot of people who liked to PLAY our game, but not work on it. I’ve had time to think about why. I figure that it’s because a) it’s a puzzle game, which scares designers, and b) it’s not terribly exciting. It’s a slow paced thinking game, with design heavy work, and a lot of art assets required (to fill out the real world).

Long story short, we had a conflict with Protocol Aurora over a member. That led to a hectic weekend. But we solved it. They have 3 excellent level designers who are going to assist us making levels. It’s a joint venture in some regards, and certainly risky, but I feel confident going into it, it’s going to help both teams.

Finally, we figured out our new team. We went from 4 official team members (I say official because Ren is not “technically” on our team) to 8. We added 2 new designers, Sarah Weber and Joey Zika, as well as Amanda Ledwidge, our new artist, and Brandon Cote, my programming partner in crime.

We have a lot of big plans for Sojourn in the upcoming semester, including an art overhaul, expansion of systems and levels, and tentative plans for publishing. It’s gonna be a longer journey than the one we just took, because at least this time we’re starting with a project in mind, instead of choosing it by week 5.

I guess this was kind of a “looking forward,” but I think that’s alright.

Last thing to do is let you try Sojourn out for yourself. From everyone on Tierceron, we hope you enjoy.

But fair warning, it’s not too well optimized, so slower computers may experience some bugs. Gotta make some sacrifices with small amounts of time.

Sojourn

Advertisements

The Tank Battler: The Wrap Up

Results are… varied. And by varied, I mean, well… let me explain the project further.

First off, it was overscoped. Not anyone’s fault honestly, it was a really cool idea, have a variety of players with a team of tanks to drive around and fight each other. But it took second priority to capstone (the student who programmed it was on one of the capstone teams, so he had trouble finding all the time to do both I’m sure), and didn’t have a designer looking over it.

We’ve been told this project will take a hiatus for a year, and may just get scrapped altogether, or get moved to Unity, which would fix a lot of the technical issues and make it easier on us developers.

That said, results came back varied because of a number of the above issues, mainly with people’s DLLs hitting breakpoints or otherwise not working, and the fact that with just a few people actually able to play, it was difficult to determine an exact winner (on top of a scoring system few of us paid attention to at all).

I didn’t go overboard on mine. It does well for what I made it do, which was mainly pathfind around. It moves around the map at random coordinates, mainly with the intention of getting away from the others. The idea being that if I could stall the match out, back myself into a corner, I might be able to take them down before they take me down.

I kind of wish I had gone more into it, but between the system problems (the unit circle was backwards and it was so so awful to work with, but that is apparently just part of SDL (which in itself baffles me)) and capstone, my drive for this final project was lower than it normally is.

Looking back at the semester, I’m still super happy with how minesweeper turned out, and I think my Gin Rummy did better than what it “scored,” so overall, on the two more structured projects, I did fairly well.

I think the class can only get better, and because our professor is really good at listening to feedback (both for this class, but he’s also the head of the game programming division, so hopefully the feedback we gave will actually change the curriculum as a whole for the better down the line), I think it turned out alright.

Our entire class has just kind of been a step behind in it’s own right. We learned flash, which got phased out for Unity the literal semester after us, and we had an entire class on XNA, which I doubt I’ll ever touch again, but in the end we’ve still put out some of the best work this college has seen to date. This should probably go in my postmortem for capstone, but I’ll put it there as well.

I guess to wrap up, all in all, it went well. Little bumpy at the end, but a good semester. And when it comes down to it, I don’t actually mind being a guinea pig, as long as the future will get better for it.

Guess Who’s Been Busy

That’s right, it’s me. Capstone has been very taxing on my time, leading to a backup in blog posts.

Quite honestly my preferred method of blogging would just be to update you on everything we’ve done in a style more fitting of “Here is our game as of now, there are many changes,” but I’ve got to have a certain number of blog posts for the class. As such, I’m just gonna keep doing what I’ve been doing, but with updates for each week that we’ve done so far.

That said, week 10, wherein I:

  • Culled NPCs
  • Added a sound manager
  • Allowed vertical movement (for our ship painting, you can climb on the nets to go up)
  • Added functionality for a variety of painting interactions, using the cannon present in the ship painting and the well in the farm painting.
  • And yet more bugfixing

It was on the lighter week, and unfortunately because of how much more work needs to be done this week (presentations on Monday), I’m not going to do pictures.

It’ll be a little dry and boring, but there are more pressing matters to attend to.

So I culled NPCs, which basically means there’s an NPC system set up. For our game, it’s simplistic, we don’t have many NPCs, but if the forest painting has the potential for an NPC, he only appears in designated paintings, thus preventing every forest painting from having an NPC.

Nothing terribly exciting about the sound manager, that was just my first attempts at putting a wrapper on Unreal’s sound system to make it a bit more designer friendly and centralized. Has a play sound function, real fascinating stuff.

The vertical movement was the first iteration of allowing our player to travel up and down. It involved a lot of switches to gravity and movement before I decided on using Unreal’s character movement modes, and just setting the character to flying. Later we created a space painting that utilizes this mode.

Now the painting interaction functionality was the big one for that week, and core to the game. To properly have it so puzzles weren’t just used to travel between each other, we had to have the meta puzzle of the room, and then mini puzzles that you use to solve a larger puzzle. So the cannon interacts with all the different paintings – blowing open new passageways in the farm and snow paintings, tilting an adjacent ship painting, tipping a tree in the forest painting, etc, and the well does things like puts out the campfire in the forest painting, creates an ice block in the snow painting, and recursively triggers wells below it.

There’ll be more updates on how the week’s have gone soon, but as of now, sleep is pretty necessary to retaining a stable headspace. Until then.

The Tank Battler, Combat: Prep

As stated in the last post, our Artificial Opponents class is wrapping up with an in house tank battler.

We already completed the movement part of that (kind of). Honestly, Capstone’s been eating away at most of our times (“our” being the collective class, including myself), so the quality of my movement AI and others was significantly weaker than it would’ve normally been.

Luckily (or unluckily, depending if our Capstone game makes it through), the final presentation is next week, so we’ll have more time to work on Artificial Opponents as Capstone moves to the backburner for the end of the semester.

As for the actual tank game, and plans – well, the first step is to fix the movement and actually get it working properly. That’ll be critical.

For the combat section, I figure I’ll be casting map-wide lines to check for friendly/enemy tanks in front of me. Fire if enemy, don’t fire otherwise.

I was imagining I’d have all 4 tanks line up in a diamond-square formation and just rotate as a massive supertank, covering all four 90 degree directions, that way the time to rotate in a circle is automatically cut by a quarter. It’s a big demand, I don’t know if I’ll be able to do that, especially if two tanks at different angles demand my attention, and I need to break them out of their supertank formation, destroy their enemies, then reform and continue moving.

I haven’t yet decided if this strategy will be best for attack or defense, but that will depend on how the scoring system is determined. If it’s best to go out, destroy tanks, pick up cash and spend it for better parts, I’ll go for that loop. If sitting in a corner and killing anyone who gets near using a spider-like supertank works best, I’ll go for that.

Only time will tell really, but for now, it’s all coming up Capstone, gotta get back to that. Until next post.

The Tank Battler, Movement: Prep

The final project for this semester’s Artifical Opponents class is a custom-made tank battler, wherein you control 4 tanks all at once and attempt to take out 3 other teams of tanks on the map (for a total of 16 roaming tanks).

For the first part, we have been tasked with getting the tanks to move. This ends up being more complicated than it sounds because the tanks don’t move on a standard “I want to go from position A to position B,” where you just follow the path. Instead, the tanks move using two treads, which each have a power level that you alter.

To turn with the tank requires uneven amounts of power, but either way, that will take experimental time to basically nail down how much power is required in each tread to get it to move as I want.

The plan as stands is to use A* for pathfinding. It’s a popular, strong, and simple pathfinding algorithm to use, and because the tank battler as a whole also has things like shooting and buying equipment, I don’t want to get bogged down in just the movement system. For this first part, movement is all that is necessary.

As for determining treads, I figure I’ll create a function that takes in the degree that the tank wants to turn, and from there factor that into two workable power tread levels, for a period of time. To turn 90 degrees to the right, I’d want to power the left tread forward at full and the right tread backward at half. Or so I think.

As of now I don’t see a reason to not make turning as fast as possible. If I can mange to spin my tanks 180 degrees in a quarter of a second, I would imagine that gives me a tactical advantage than doing it over 2 seconds. But if I manage to do that, others will too, so it might determine how I create AI later, when shooting and inventory come into play.

Anyhow, next up is to actually create it. See you in the report post.

Make It Snappy

A lot of work was done this week, and it was a bit of gameplay, a bit of feedback, and we have so much more to do to fully flesh it out, but I’m feelin’ good after this week.

Let’s hit the checklist of what got done:

  • Implemented a walking character animation
  • Gave the gateway on the loose hook a swing “animation”
  • Gave the gateway falling off a loose hook a full camera transition, including screenshake
  • Added a raycast blocking wall for puzzle design
  • Implemented a top tier Programmer Art™ pause menu complete with two entire buttons (very advanced)
  • Added a transition upon activating a real world connected interactive object within a painting
  • Added bidirectional asset attachment for design ease
  • And a lot of bugfixes (which I won’t cover because they don’t exist anymore, which is good)

And now for the visual part!

Walking character animation

WalkAnim

Gateway Swinging

Swing

Gateway Falling Transition

Fall

Raycast Blocker (The painting on your side of the gate is accessible, but painting through the gate is not)

MagicWall

Pause Menu

PauseMenu

Interactive Object

Interactive

And the bidirectional asset attachment is nothing fancy. It’s basically that when a gateway is assigned to a hook, the hook is assigned back to the gateway. It cuts out the time the designer has to hunt through the objects and set them all to each other, and instead only has to set one object.

There’s a lot more getting done every week, and next week will have even more. Until then.

The Critical Factor

Time. The critical factor in most everything is a deadline.

We’ve got a little under a month left to go, and we’re finding a balance between implementing gameplay bits and feedback bits. This past week was gameplay, here’s the rundown of all that I put in.

New hook type, loose. Doesn’t quite have the feedback, so no gif for this one, but what it does is that when you enter a painting on a loose hook and there is a hook underneath it, which does not have a gateway, and then fall with enough velocity, your gateway will fall down to the lower hook. A one-way downward travel system.

New frame type, wall scroll. Wall scrolls are the fun “paintings within paintings” meme that is brought up now and then. When you take a wall scroll into a painting, you have an in painting representation, and when you pop out, the painting comes with you. Fairly straight forward, but may add puzzle complexity if you need to handle multiple objects (still in debate).

Another technical new frame type (though technically the frame is really just an indicator, nor do we have the model in yet), Interactive. In an interactive painting, an object can be found that is linked directly to the real world. For example, we have a door on a pirate ship that when interacted with opens a door in the real world.

And the final bit was finally the implementation of our painting camera zooming in on the character and following them around, Mario style-ish. It stops on the left and right edges, but follows the player up and down as well, so the zoom is a little more close in than Mario. This was actually a bit I had held off for a few weeks, mainly because there were better things to do, but I’m also generally not a graphics programmer, so this area is less intuitive for me.

That said, this post is off the normal schedule, so I can update on a tiny few things planned for this week. It’s mostly feedback, because we need to clean up the transitions as well as add gameplay feedback for things like the loose hook. I also get to mess with transitions again (oh god) and create some invisible terrain that blocks the player from reaching paintings/hooks. Until next week.

Playing Gin Rummy: The Wrap Up

Like the Minesweeper post, this won’t be nearly as fun as the “This was my process” post. Instead, it’s just a postmortem on how I thought it all went.

This time I didn’t do as well as I’d hoped. It’s hard to rank the AIs, because we did a double elimination bracket system, so I didn’t get to play against everyone, but there were a few consistent factors.

My AI was roughly at the higher end of the middle of the pack. I could beat most AIs, but a few people had AIs who were consistently better. I actually lost to the same person in both rounds of my losses in very close matches that could have gone either way. In the round where I got knocked out, if I had won, I would have beaten him, it was that close.

There were some AIs that played less conservatively with their cards and would remove high cards from their hand very fast. In cases where they would get Gin, my AI might be sitting on a few dozen points. Needless to say, those AIs generally beat mine.

Compared to AIs that had a similar strategy to mine, it did generally well. A few had a similar strategy to my own, doing things like removing pairs of Kings and keeping single cards of low cost.

Alan once again was the definitive winner. His AI never lost a single match against anyone else (for clarification, a match was 40 games). I asked what he did, and he used a similar base as my own, but improved it with weights on discarding certain cards. As he fine tuned the weights, the AI got better.

I do wish I had added that myself, but time was unfortunately sparse, the capstone project consuming the majority of my time.

Either way, the next project is a custom made Tank Game, which is thoroughly complex (to code, it’s fairly simple to actually play), so that will be an interesting challenge. Because it’s custom made, right now it’s got a lot of bugs. Certainly going to be interesting with all the fixes coming in throughout. Until then.

Playing Gin Rummy: The Good Part

Intro

First off, since the last post, a new condition for the game was added: passing. Passing occurs right after the hands are dealt but before the first player makes their move, wherein both players trade a single card to each other.

Now then, much like with Minesweeper, all the AI logic used here is entirely my own, which feels more natural given that Minesweeper makes heavy use of algorithms, where Gin is more of a weighted system when determining which card you want to pick up or discard.

I won’t include a results section, as there’s no real test I can run purely against my own AI. Every time I want to play, I have to play against another classmate’s AI (or the really awful default AI), and my success can only be measured in comparison to theirs.

That said, my initial testing (and I tested against every single AI available to me) was fairly positive. I did very well against most people, with two individuals (Alan, who took first place in Minesweeper, and James, who placed first in the initial dry run (which to be fair is very preliminary, many AIs were not ready at the time)) I measured specifically against. With Alan, his AI is all over the place, and I’m not sure why. One series of games it would go from doing very well to doing very poorly, with no set pattern. James was more consistent, but in my favor, I was able to beat him 4 out of 5 times.

The Process

For this post, partly because of length, but mainly because posting code feels rather pointless (It’s our professor’s custom built engine, so while readable, copy-pasting would be useless), I’m just going to cover the step by step process I take through playing Gin.

Disclaimer: I will include parts that are not in use (but if I improved the AI further might’ve been). This AI is not fully optimized, it does not take probability into occurrence, nor does it find pairs among two separated cards (5 of spades and 7 of spades are not considered special by the AI unless a 6 of spades appears).

Notable Variables, Structs, & Functions

Variables

  • UINT myPlayerNum, keeps track of the player number (wow who could’ve guessed)
  • bool justReset, boolean for determining when the game was just reset, gets turned on once and then off in the first draw phase after an update
  • vector<Card> hasBeenSeen, a master list of all cards seen (Does update, but is not used; could be for probability)
  • vector<SetOrRun> lockedSORs, a list of all sets or runs, all cards in this list are not allowed to leave the hand
  • vector<Pair> heldPairs, a list of all pairs (both of potential runs and sets), cards in this list are only discarded if there are no “single” cards
  • vector<PCard> handPriority, a list of all cards in hand, each is given a priority number of 0, 1, or 2 (high to low priority)

Structs

  • Pair, keeps track of 2 cards, whether they’re the start of a set or run, and overloads the == operator to compare pairs
  • SetOrRun, keeps track of 3+ cards, whether they’re part of a set or run, and overloads the == operator to compare SORs
  • PCard, a struct that holds a card and a priority number

Functions

  • bool completesOrAddsToSetOrRun(Card c), determines whether a given card completes or adds to a pair or a SOR, true if it does
  • void cullDiscardFromPairs(Card c), removes all pairs that contain the discarded card from heldPairs
  • Card getCardToDiscard(), browses the hand and finds the highest priority card (a 2 if possible) with the highest value
  • void setHandPriority(Hand h), adds all cards in hand to handPriority with an associated priority number, clears the previous handPriority list when called

Step by Step

  1. Upon resetting the game
    1. Clear all lists (hasBeenSeen, lockedSORs, heldPairs, handPriority)
    2. Add the discard to hasBeenSeen
    3. Set justReset to true
  2. Determine passing step
    1. updateOnReset, which:
      1. Sets the player number
      2. Adds your hand to hasBeenSeen
      3. Finds any sets or runs in hand and adds them to lockedSORs
      4. Finds any pairs and adds them to heldPairs (includes pairs that are apart of SORs)
      5. Removes any pairs that are in SORs (so as to not trick your own system)
      6. Set hand priority
    2. Get the card to discard
    3. Cull the discard card from pairs
    4. Return the chosen card
  3. When it comes to your turn, determine whether you’ll draw from the deck or discard
    1. Update, which does everything that updateOnReset does except for setting the player number. However, it also checks for duplicates on each list, and does not add any cards to hasBeenSeen, pairs to heldPairs, or SORs to lockedSORs if they already exist within the list. It also assures that if pickedUpInitialDiscard is true, then do not access the top of the discard (because there is none)
    2. If completesOrAddsToSetOrRun is true, take the discard. Otherwise take the deck
    3. Flip a few reset variables (will only happen once)
      1. If justReset is true and you chose the discard, flip pickedUpInitialDiscard to true. This was a fix for this particular system, but it avoids the issue in the next update wherein you check the top card of the discard, and if its empty (because you just picked it up), it won’t throw an error
      2. If justReset is false and pickedUpInitialDiscard is true, flip pickedUpInitialDiscard back to false
      3. If justReset is true, flip it to false
    4. Draw your card
  4. Now you must discard
    1. Update to redo calculations for your newly drawn card
    2. Get the card to discard
    3. Remove that card from your hand and score your hand. If you are under the knock threshold, take the knock (set to true), doesn’t matter what value (our default value is 10)
    4. Cull the discard from pairs
    5. Discard your card
  5. Repeat steps 3 and 4 until the game ends

It’s a lot more compact that I expected to write, but there’s a lot of backend functionality that doesn’t warrant a spot on the list.

Functions that determine sets and runs from your hand, pairs, checks for duplicates among lists, checks the best form of scoring (whether it be from taking the 5 of hearts, 5 of diamonds and 5 of clubs, or the 5 of clubs, 6 of clubs, and 7 of clubs as your set/run), breaks apart a list of cards returned as SORs into the actual SOR struct, a lot goes on to actually run the base update function, which is where most of the information cataloging is done.

The deterministic functions, which get the card to discard or checks if the card completes a set or run end up using those lists of information to check whether they want a given card or not.

Final Thoughts

I predicted it last time, and I’ll be bold and predict it again: I think my AI will do well. I’m quietly hoping that it will place first this time, but just as Alan dark horsed his way in last time, there’s always that possibility that someone will have a strong AI that gives challenge.

I also don’t have a unique algorithm that’s leading the charge, just a series of logical choices that I as a player would make (albeit as a player I might use more nuance in some decisions, but god damn if nuance isn’t the trickiest thing to program), so I feel less confident that I will do well this time (but still fairly confident).

I guess we’ll just have to see how it all shakes down tomorrow or Friday, whenever the actual competition is. Till the next post, thanks for reading.

When The Masses Ask…

The big area focused on this week was player feedback, as we got a lot of QA responses that wanted to know more information about the world and their physical relation to it, especially when travelling through paintings.

The big two areas that we improved feedback through was via a render texture mesh that we added to the gateways in the real world (this is of the forest landscape):

RenderTexture.png

And by adding a camera transition in two parts: moving in/out of a painting, and between paintings:

InOut

Between

The other bit of player feedback attempted to expand on was the narrative. Besides just fleshing it out in the week prior, we implemented a very basic NPC (can be seen as the red block in the gifs above – very creative I know), that when you walk past says a bit of dialogue to you. Nothing fancy for now, but paves the way for easy implementation later.

Finally, the two bits of functionality we threw in for this week were a new frame type, the latched painting, which is a frame that can be dived into but not moved from it’s location, and having interactive objects within paintings.

This is our latched painting:

Latched.png

The interactive objects, first off, replace the collider that it’s on the side of. For example, our snow painting has an igloo on the left side, which counts as our interactive object that disables the left collider travel option (you can still collide, you just won’t travel between paintings), and you will only travel upon hitting E (which later will be accompanied by an animation).

Interact

This next week’s focus is on improving gameplay, mainly in the painting world, but also adding new hook and frame types to add puzzle variety, so tune in then.