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.

Advertisements

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.

Infrastructure Is Key

We’ve taken our Painting game, which still does not have an official name, and are working toward fleshing it out considerable. Giving it pizzazz where we can, and working toward that final vertical slice for the end of the semester.

My main area of focus, being the first week of solely working on just this one game, was on improving the infrastructure of the areas I more quickly set up, and cleaning up our repository so it doesn’t take four centuries to download.

Repository cleanup went well, I’m somewhat anal in my organizational formatting for the folders that I use and access, so I like to have things easy to find and not all over the place. Helps me at the very least, I like to imagine it helps others too.

Matt (Designer) added a third, more complex puzzle, and a background piano track that he composed himself. It’s a little simple, but it’s only the start, I expect great things.

We got two bits of player feedback in. First, when you are holding a painting and are near enough to place it on a hook, the hooks glow. Considering our hook model is somewhat visually diminished, I found it pretty helpful when I was testing the levels. It does help that Tucker (Artist) put a nice background to accentuate the “You can place a painting here!”

HookGlow.png

The second bit is the frame’s give you a direct visual indication of where they’re connected to. Easier to show then tell so…

EdgeGlow

And that updates on the fly as you go.

Final bits worth noting are a variety of bugfixes and infrastructure improvements. Three of note:

  • Each painting type now has far easier designations of entrances and exits on all four sides.
  • Paintings would occasionally stay highlighted when they shouldn’t and only one object could be highlighted at a time, both of those are now fixed.
  • The distance at which you can pick up/put down a painting is differentiated from the distance at which you can dive into a painting. Good for some puzzles. Matt has a puzzle with a gate that enforces that limit. In the future we’ll have different painting and hook types so we don’t need direct physical barriers.

That’s this week. We’ve got a lot planned for next week, so check in then.

The Chopping Block

The pain of working with full 3D physics is over. Snails have defeated me.

But honestly I’m not sad at all. While it was a challenge, it taught me a lot about Unreal as an engine, both in blueprints and with their C++ code base. And I even went to a professor who was super helpful in terms of teaching me about quaternions and how they work (much thanks Dan).

On the positive note, we have decided to move forward with the Painting game (name still pending), our Gothic themed puzzler!

To show off some highlights, here is our starting room:

FirstRoom.png

And when you near the paintings, they glow to signify that you can carry them!

ezgif-3-2b5a4f125a

So far, our first two painting types, forest and snow:

ForestPainting.pngSnowPainting.png

The little copper-ish capsule (placeholder) is your character, who moves around the paintings and bumps into walls to switch between paintings.

And finally I’ll show off the (technically second) puzzle, where you have to grab the two paintings on the walls and move them to the hooks (which are the stone squares on the wall right now). That allows you to dive into the bottom painting, maneuver your way into the upper painting (by using the tree and cloud in the foreground of the forest painting) and then go right to appear in the painting on the balcony, which you can then pop out of, and you’ll have solved it!

ezgif-3-f6a8a24b04ezgif-3-ea5943310b

Overall, super happy with the progress made, it’s a fairly solid initial prototype. We’ll be looking to challenge this week, so ideally that will succeed and put us into deep dive where we can take this game and push forward with it even farther (though we’ll end up doing that anyways, but professors’ blessings are always nice).

Final bit, if you want to try out the prototype, it can be found here!

More to come in another seven days!