We have a new update on The Future of the Penny Arcade Forums.

Game Dev - Unreal 4.13 Out Now!

18283858788100

Posts

  • GlalGlal AiredaleRegistered User regular
    Also, this is me with Blueprints all the time.

  • HandkorHandkor Registered User regular
    We do have new blueprint alignment tools in the right-click menu.

  • GlalGlal AiredaleRegistered User regular
    Neat! Do they also give us the ability to move input/output pins along the side to better fascilitate the alignment? Or swapping their positions, so I don't have the branch True and False outputs cross when the wrong one is the "just go straight on"? Or a vertical node reroute? Am I missing any of those?

  • HandkorHandkor Registered User regular
    Sorry it's not that fancy. Here is an example from Michael Allar

    AlignSample.gif

  • GlalGlal AiredaleRegistered User regular
    edited February 2016
    I am so stealing those keyboard shortcut ideas.

    [edit] Does anyone else have an issue where docs/answers.unrealengine.com pages seem to either take forever to load or need multiple attempts to load? For the past few days I've had to stop and manually set a page to reload 2-3 times every time, just to guarantee the damned thing loads within a reasonable time.

    Glal on
  • KupiKupi Registered User regular
    Zek wrote: »
    Man sometimes my brain is so fickle when it comes to motivation and I don't even know why. A couple weeks ago I found out Cookie Clicker 2.0 was out and I've been going down that utterly pointless rabbit hole again - what I've discovered is that it seems to occupy the same space in my brain that my game development hobby does. I had trained myself to take those "I'm bored at my computer right now" moments as an impetus to do some dev work instead of aimlessly browsing YouTube or something, but now I feel the pull of tabbing over to Cookie Clicker to check my progress and buy some more upgrades, and after that the moment is gone. It's like my mind only has so much capacity for multitasking, and having a timesink like that floating on the edge of my awareness at all times pushes it just over the edge.

    Anyway I resolved to free myself from the idle game curse ASAP by way of heavy automation, so I will put it to bed once and for all any day now.

    Edit: Screw it, I deleted my save. This far and no further. Fuck you, lizard brain.

    I have kind of avoided posting anything similar to this on account of not wanting to seem like a purveyor of sour grapes given the successes other people are having in this thread, but fuck it, I'll go in on it just to say that you are not alone in this. After abandoning a project that I'd spent about two years of my life on (to no result), I've been in a several-month slump. I've been doing everything in my power to make my life boring in the meantime-- I run a plugin for FireFox called LeechBlock that lets you ban certain webpages at specific times of day, packed up my radio, put a freeze on game purchases (especially roguelikes and an MMO I used to play; any "unlimited play" games are a hard no), and so on. I am not to the point where I've actually resumed doing the thing that Young Kupi said he was going to spend his life doing, but it's slowly coming back.

    All this to say that I think you're doing the right thing. Keep at it!

    My favorite musical instrument is the air-raid siren.

    I'm "kupiyupaekio" on Discord.
  • KashaarKashaar Low OrbitRegistered User regular
    Y'all just need to make it a habit! Which takes a certain amount of initial energy, and then some low-energy upkeep every now and again when you're slipping. Conscious habit formation is one of the best and most useful skills I've ever learned in my entire life.

    So.

    Go and find a timeslot in your lives that you can devote to doing this thing, and then every day/week/weekend/whatever, do this thing even if you don't feel like it, even if you hate it, even if you think you'll never amount to anything, even if you think it's pointless, even if you reeeeally don't have the energy for it. You're not doing the thing for its own reason, but so that you'll form a habit of doing the thing. After a short while, doing the thing will just come naturally to you, and it will be your new reality.

    Motivation is completely unreliable - it comes and goes as it pleases, and is influenced by a whole bunch of factors such as how nice the weather is outside, if you're nursing a crush on somebody you met at the club last weekend, whether your favorite show just got a new season released on Netflix... So if you want to get shit done, you need to forget about motivation and concentrate on discipline. Motivation is fickle, but discipline is something you and only you control entirely.

    So, what are you waiting for? Go do the thing:

    Indie Dev Blog | Twitter | Steam
    Unreal Engine 4 Developers Community.

    I'm working on a cute little video game! Here's a link for you.
  • HallowedFaithHallowedFaith Call me Cloud. Registered User regular
    Kashaar wrote: »
    Y'all just need to make it a habit! Which takes a certain amount of initial energy, and then some low-energy upkeep every now and again when you're slipping. Conscious habit formation is one of the best and most useful skills I've ever learned in my entire life.

    So.

    Go and find a timeslot in your lives that you can devote to doing this thing, and then every day/week/weekend/whatever, do this thing even if you don't feel like it, even if you hate it, even if you think you'll never amount to anything, even if you think it's pointless, even if you reeeeally don't have the energy for it. You're not doing the thing for its own reason, but so that you'll form a habit of doing the thing. After a short while, doing the thing will just come naturally to you, and it will be your new reality.

    Motivation is completely unreliable - it comes and goes as it pleases, and is influenced by a whole bunch of factors such as how nice the weather is outside, if you're nursing a crush on somebody you met at the club last weekend, whether your favorite show just got a new season released on Netflix... So if you want to get shit done, you need to forget about motivation and concentrate on discipline. Motivation is fickle, but discipline is something you and only you control entirely.

    So, what are you waiting for? Go do the thing:

    I am so motivated now... to do the thing! Yes! I will do it! WAAAAAAAAAAAAAH!




    SFaY2zJ.png

    I'm making video games. DesignBy.Cloud
  • rembrandtqeinsteinrembrandtqeinstein Registered User regular
    Zek wrote: »
    Man sometimes my brain is so fickle when it comes to motivation and I don't even know why. A couple weeks ago I found out Cookie Clicker 2.0 was out and I've been going down that utterly pointless rabbit hole again - what I've discovered is that it seems to occupy the same space in my brain that my game development hobby does. I had trained myself to take those "I'm bored at my computer right now" moments as an impetus to do some dev work instead of aimlessly browsing YouTube or something, but now I feel the pull of tabbing over to Cookie Clicker to check my progress and buy some more upgrades, and after that the moment is gone. It's like my mind only has so much capacity for multitasking, and having a timesink like that floating on the edge of my awareness at all times pushes it just over the edge.

    Anyway I resolved to free myself from the idle game curse ASAP by way of heavy automation, so I will put it to bed once and for all any day now.

    Edit: Screw it, I deleted my save. This far and no further. Fuck you, lizard brain.

    Good jorb. I recently deleted a marvel puzzle quest save that had maybe 500 hours into it. And losing my old phone with my similar hundreds of hours puzzles and dragons save was one of the best things that could have happened to me.

    My policy now is I won't play any game with an time-based "energy" system. And I'm limiting my exposure to anything with "daily" rewards and paid blind bags (my exception being hearthstone but I only play arena).

    Giving up these progression games is like giving up soda. Its a product that is designed to make money by tricking your brain into producing pleasure chemicals. The games stimulate the feeling of accomplishment by trading time for increasing numbers. They are seductive because they give that feeling without any "real" risks and with minimal frustration.

    In conclusion growing up sucks.

  • rembrandtqeinsteinrembrandtqeinstein Registered User regular
    this is more of a general software design philosophy question than game specific: In your opinion is better to have a single monolithic "brain/overlord" object that everything else talks to, or is it better to have objects talk to each other directly?

    My concrete example. In my application I have 3 objects, a "main" that starts everything, a UI controller that displays a window for the user to poke and a data handler that interacts with the db.

    When the UI window is resized by the user I want it to save the new size in the database, and when the application is re-opened I want the window to open to the previously saved size.

    Should I have my UI controller directly talk to the data handler or do I have the UI controller talk only to main?

    Having objects only talk to main has the advantage of being able to easily change the architecture, having the objects talk to each other makes logical sense and keeps object specific functions more "encapsulated".

    I lean toward more encapsulation even if that makes for a spiderweb of object interaction.

  • RendRend Registered User regular
    this is more of a general software design philosophy question than game specific: In your opinion is better to have a single monolithic "brain/overlord" object that everything else talks to, or is it better to have objects talk to each other directly?

    The first thing you are describing is a God Object and good design should avoid it at all costs. It violates a whole lot of principles, such as encapsulation, open closed, single responsibility, demeter's arrow, several more as well.

    The spiderweb of interaction you're talking about actually can probably be mitigated by ensuring your design only requires one-way dependency. For instance, in chess, let's say you have a board and several pieces.

    If the pieces know about the board and the board knows about the pieces, that's circular dependency. If the board knows about the pieces but the pieces do NOT know about the board, that's one way dependency. Aggressively eliminating circular dependency in your code will significantly reduce the spider web thing that is common in overly dependent code.

  • rembrandtqeinsteinrembrandtqeinstein Registered User regular
    Rend wrote: »
    this is more of a general software design philosophy question than game specific: In your opinion is better to have a single monolithic "brain/overlord" object that everything else talks to, or is it better to have objects talk to each other directly?

    The first thing you are describing is a God Object and good design should avoid it at all costs. It violates a whole lot of principles, such as encapsulation, open closed, single responsibility, demeter's arrow, several more as well.

    The spiderweb of interaction you're talking about actually can probably be mitigated by ensuring your design only requires one-way dependency. For instance, in chess, let's say you have a board and several pieces.

    If the pieces know about the board and the board knows about the pieces, that's circular dependency. If the board knows about the pieces but the pieces do NOT know about the board, that's one way dependency. Aggressively eliminating circular dependency in your code will significantly reduce the spider web thing that is common in overly dependent code.

    Thats pretty much what I'm trying for with a "component" based system. Where the components are logical abstractions of the application function. I have a UI controller and a data handler. The UI controller needs to know default window sizes which are kept in the database so it asks the data handler. They know each other because each is a static member of the "overlord" class.

    What I generally go for is anything that has does a "physical" action like resizing a window or updating to the database only talks to one other "interface" object, but the interface objects all talk back and forth to each other.

  • RendRend Registered User regular
    edited March 2016
    Rend wrote: »
    this is more of a general software design philosophy question than game specific: In your opinion is better to have a single monolithic "brain/overlord" object that everything else talks to, or is it better to have objects talk to each other directly?

    The first thing you are describing is a God Object and good design should avoid it at all costs. It violates a whole lot of principles, such as encapsulation, open closed, single responsibility, demeter's arrow, several more as well.

    The spiderweb of interaction you're talking about actually can probably be mitigated by ensuring your design only requires one-way dependency. For instance, in chess, let's say you have a board and several pieces.

    If the pieces know about the board and the board knows about the pieces, that's circular dependency. If the board knows about the pieces but the pieces do NOT know about the board, that's one way dependency. Aggressively eliminating circular dependency in your code will significantly reduce the spider web thing that is common in overly dependent code.

    Thats pretty much what I'm trying for with a "component" based system. Where the components are logical abstractions of the application function. I have a UI controller and a data handler. The UI controller needs to know default window sizes which are kept in the database so it asks the data handler. They know each other because each is a static member of the "overlord" class.

    What I generally go for is anything that has does a "physical" action like resizing a window or updating to the database only talks to one other "interface" object, but the interface objects all talk back and forth to each other.

    You might try allowing each component that needs to get data from the db to have its own interface into it, instead of having one common component that talks to the database. Not custom, of course, but each logical component having an instance of a database interface class. Classes that act as interfaces to external resources are typically pretty lightweight, especially if you're only going to use it once for initialization and then throw it away. That saves you the trouble of trying to find a reference to the DB interface, whether that's through a UIController, a singleton, or what have you.

    Or to ask the question: Is there a specific reason that only one instance of one class in your code is allowed to talk to the database at a time?

    Rend on
  • rembrandtqeinsteinrembrandtqeinstein Registered User regular
    I was planning on using DB connection pools in the future. I don't think this project will be DB heavy enough to need it, just to for my own practice. Right now I'm using a connection pool of 1 because I'm only hitting the DB in the main thread but that will change as I add more stuff to the project. The DB interface takes a logical method name and returns the appropriate data structure. The example for the window thing is the DBinterface has a "getmainwindowproperties" and returns a rect that has the left, top, width, and height values.

  • RoyceSraphimRoyceSraphim Registered User regular
    Threw myself back at gamemaker and Shaun Spalding's tutorials.

    Made me smile to jump on an enemy and watch it pop.

    Then I moved onto a falling phobic enemy tutorial and confused + for * and saw my height phobic enemies start bouncing off of walls in a really cool fashion.

    steam_sig.png
  • MachwingMachwing It looks like a harmless old computer, doesn't it? Left in this cave to rot ... or to flower!Registered User regular
  • rembrandtqeinsteinrembrandtqeinstein Registered User regular
    So apparently Swing is old and busted and JavaFX is the new hotness.

    Anyone here use JavaFX and feel like point out some landmines before a nub like me trips on them?

  • amnesiasoftamnesiasoft Thick Creamy Furry Registered User regular
    I only used JavaFX a little bit for a school project. I don't recall running into anything particularly troublesome, but someone else that uses it more might feel otherwise.

    steam_sig.png
  • KashaarKashaar Low OrbitRegistered User regular
    Machwing wrote: »

    HELL YAS

    #GDCHYPE

    Indie Dev Blog | Twitter | Steam
    Unreal Engine 4 Developers Community.

    I'm working on a cute little video game! Here's a link for you.
  • LaCabraLaCabra MelbourneRegistered User regular
    I wish I could afford to go this year :(

  • KashaarKashaar Low OrbitRegistered User regular
    LaCabra wrote: »
    I wish I could afford to go this year :(

    Next year, Joe. I demand beers!

    Indie Dev Blog | Twitter | Steam
    Unreal Engine 4 Developers Community.

    I'm working on a cute little video game! Here's a link for you.
  • RoyceSraphimRoyceSraphim Registered User regular
    New tutorial, new fuckups.

    angel != angle

    steam_sig.png
  • KhavallKhavall British ColumbiaRegistered User regular
    edited March 2016
    So I managed to get the drum and bass towers working to do their cool cooperative playing of a single line, and I managed to hook up all the audio in a way that it works, and I got all the notes working so that they'd play correctly.

    And then I started noticing that, like, something was definitely wrong. The towers were shooting super slowly for what they should be doing. Not just more slowly, but they're firing as though they're playing quarter notes instead of sixteenth notes.

    There are other problems too that are easy fixes, but definitely this one is causing me some headaches.

    So I debug.log the fire Rates, to see if for some reason they're just never hitting 1 and.... nope they totally are getting to the point where they should be at full tempo when fully upgraded.

    Finally it dawns on me..... Tempo(in beats per minute), divided by 60, does not get the number of seconds each beat takes. It gets the number of seconds each measure takes.
    All I needed to do was, like, the most basic of basic arithmetic, combined with maybe just the tiniest bit of musical knowledge. Whoops.jpg


    On the other hand, the music actually ended up being kind of metal.

    https://youtu.be/C1W3bcQjT6g



    Also also I reeeeally wish I had caught this before doing a big ol' balance pass

    Khavall on
  • RendRend Registered User regular
    So, in preparation for an imminent spike in my game's complexity (jumping from prototype to actual real stuff), I decided to pay off my tech debt by rewriting it entirely using test driven development. Basically, that means only ever writing code when there's an automated test that requires that code to pass.

    And holy cow the simplicity and elegance of design that this methodology tends toward is incredible. I was skeptical about it due to unity weirdness but I could not possibly be more pleased with hope it's going.

  • DarkMechaDarkMecha The Outer SpaceRegistered User regular
    edited March 2016
    It's funny, now that I have a local avoidance system that generally works well, even if it is abit jerky and needs a prioritization element added to it, I begin to question...do I need this? I started out building a top down space combat sim but I've been thinking about what's more fun and fluid. I find myself taking away sim elements and trying to laser focus the gameplay on the actual tactics of space war from a top down perspective. Fully dynamic collision between ships and the environment (asteroids ect) is starting to feel more like a needless complexity that slows the game down for little gameplay benefit. I need to do more testing, but I might either limit it to ships vs the environment but have no ship-to-ship collisions, or ditch collisions entirely like I was originally planning to.

    I was playing Galak Z and watching how the AI handled pathing and avoidance. Much to my surprise, most of the time it has apparently no avoidance methods - ships just bounce into each other if they hit. And the game is no worse for it. Now my game is more complex and "naval" in it's movement style, so that may not work. I think the lesson is that you don't always need a thing just because other games have it. More pathing / avoidance experiments to follow.

    DarkMecha on
    Steam Profile | My Art | NID: DarkMecha (SW-4787-9571-8977) | PSN: DarkMecha
  • IzzimachIzzimach Fighter/Mage/Chef Registered User regular
    Man, I really wanted to allow the player to hang and climb on moving things. But the UE4 character movement does not really support such a thing at all.

    So I came up with a hack where during climbing the character is basically a physics object dangling there, held to the wall by a spring. Movement input doesn't move the character, but instead moves the location where the spring attaches to the wall. The characters capsule follows along using physics. When you jump or drop it switches back to the normal (kinematic) character movement code.

    Works pretty well, but the code is a maze of hacks and questionable choices. Welcome to game development! :huh:

    Here's an animation showing the character dangling on a pendulum. Such a simple thing took a lot of trial and error.

    Bpumvu2.gif

  • RoyceSraphimRoyceSraphim Registered User regular
    edited March 2016
    Speaking of trial and error, here's the code I hacked together for animation in gamemaker. Well, not animation, I just wanted to figure out how to switch the player sprite's facing based on horizontal speed (hsp) without using Spaulding's tutorial. I had watched the animation one once last year and just......wanted to figure it out on my own.
    if(hsp != 0) image_index = 2; else image_index = 0;
    if (hsp < 0) image_xscale = -1; else image_xscale = 1;
    

    That took me an hour of experimenting, after an initial prototype of about....12+ lines.

    edit: I also wasted an extra 10 minutes trying to figure out why everything was stuck at index 2 reversed, when I realised I had not programmed it to turn back to the default status.
    Khavall wrote: »
    So I managed to get the drum and bass towers working to do their cool cooperative playing of a single line, and I managed to hook up all the audio in a way that it works, and I got all the notes working so that they'd play correctly.

    And then I started noticing that, like, something was definitely wrong. The towers were shooting super slowly for what they should be doing. Not just more slowly, but they're firing as though they're playing quarter notes instead of sixteenth notes.

    There are other problems too that are easy fixes, but definitely this one is causing me some headaches.

    So I debug.log the fire Rates, to see if for some reason they're just never hitting 1 and.... nope they totally are getting to the point where they should be at full tempo when fully upgraded.

    Finally it dawns on me..... Tempo(in beats per minute), divided by 60, does not get the number of seconds each beat takes. It gets the number of seconds each measure takes.
    All I needed to do was, like, the most basic of basic arithmetic, combined with maybe just the tiniest bit of musical knowledge. Whoops.jpg


    On the other hand, the music actually ended up being kind of metal.

    https://youtu.be/C1W3bcQjT6g



    Also also I reeeeally wish I had caught this before doing a big ol' balance pass

    This was actually quite soothing to listen to.

    RoyceSraphim on
    steam_sig.png
  • HandkorHandkor Registered User regular
    Izzimach wrote: »
    Man, I really wanted to allow the player to hang and climb on moving things. But the UE4 character movement does not really support such a thing at all.

    So I came up with a hack where during climbing the character is basically a physics object dangling there, held to the wall by a spring. Movement input doesn't move the character, but instead moves the location where the spring attaches to the wall. The characters capsule follows along using physics. When you jump or drop it switches back to the normal (kinematic) character movement code.

    Works pretty well, but the code is a maze of hacks and questionable choices. Welcome to game development! :huh:

    Here's an animation showing the character dangling on a pendulum. Such a simple thing took a lot of trial and error.

    Bpumvu2.gif

    Nice idea but yeah the character class feels too rigid sometimes especially with the way it's half physics and half kinematic. It's great for FPS controls but weird for platformers.
    Sticking with 100% blueprint for custom movements is very hacky, I've had better luck using C++ for character movement hacks.

  • GlalGlal AiredaleRegistered User regular
    I can't stop watching that GIF, it's mesmerising.

  • MahnmutMahnmut Registered User regular
    Rend wrote: »
    So, in preparation for an imminent spike in my game's complexity (jumping from prototype to actual real stuff), I decided to pay off my tech debt by rewriting it entirely using test driven development. Basically, that means only ever writing code when there's an automated test that requires that code to pass.

    And holy cow the simplicity and elegance of design that this methodology tends toward is incredible. I was skeptical about it due to unity weirdness but I could not possibly be more pleased with hope it's going.

    If you ever feel moved to do a writeup or something on this -- I'm super interested.

    Steam/LoL: Jericho89
  • KashaarKashaar Low OrbitRegistered User regular
    Whoo, I'm on my way to GDC! Flight leaves tomorrow morning. So hype!

    Indie Dev Blog | Twitter | Steam
    Unreal Engine 4 Developers Community.

    I'm working on a cute little video game! Here's a link for you.
  • LaCabraLaCabra MelbourneRegistered User regular
    Say hi to our mutual acquaintances for me!

  • RendRend Registered User regular
    Mahnmut wrote: »
    Rend wrote: »
    So, in preparation for an imminent spike in my game's complexity (jumping from prototype to actual real stuff), I decided to pay off my tech debt by rewriting it entirely using test driven development. Basically, that means only ever writing code when there's an automated test that requires that code to pass.

    And holy cow the simplicity and elegance of design that this methodology tends toward is incredible. I was skeptical about it due to unity weirdness but I could not possibly be more pleased with hope it's going.

    If you ever feel moved to do a writeup or something on this -- I'm super interested.

    I just might do this on a break from actual development this weekend!

  • KashaarKashaar Low OrbitRegistered User regular
    I've arrived safely in San Francisco. Feels like this city is crawling with game developers. Seriously, they're everywhere... even the customs/border patrol lady said so!

    Indie Dev Blog | Twitter | Steam
    Unreal Engine 4 Developers Community.

    I'm working on a cute little video game! Here's a link for you.
  • HandkorHandkor Registered User regular
    edited March 2016
    Kashaar wrote: »
    I've arrived safely in San Francisco. Feels like this city is crawling with game developers. Seriously, they're everywhere... even the customs/border patrol lady said so!

    I was in New Orleans years ago for a big Microsoft conference and it was the same thing, the French Quarter was crammed full of developers. Some of the local businesses were wondering what was up with seeing only one type of clientele for a week.

    Handkor on
  • RendRend Registered User regular
    Alright, Test Driven Development (TDD) in unity. Let's do this.

    PART 0: Intro

    Get a few things right out of the gate. I'm using unity, I'm developing it in Visual Studio using C#. You have to set up a separate test project outside of your game folder or things get wonky. I'm using microsoft's .NET unit testing framework.
    I'm making a Strategy RPG like Final Fantasy Tactics or Fire Emblem, so that's what I'll focus on.

    PART 1: Why is this difficult or weird?

    So, the first thing to mention is that all of the unit tests are going to be run on the code in our game folder, but the tests themselves will not be in that folder, and they will not be running unity when the tests run. As a result, we are not going to test our scenes, because they will not instantiate. One implication of this is that everything we test must be creatable via the "new" keyword, which means we are not going to be testing any MonoBehaviours.

    "But wait," I hear you saying, impatient... judging. "All of the objects in my game world must inherit from MonoBehaviour. Am I just not going to test those?" Yes, we are going to test those. In fact, those are probably a huge selection of the most important objects to test, right? In large part, those ARE your game. So, then how?

    The important thing to know about TDD is that in object oriented systems, our objects exist in clusters that communicate amongst themselves in (sometimes) complex patterns. When unit testing, we week to isolate these objects and test them apart from other things. How do you test an object which is part of a large web of objects? You create fake objects to give it fake inputs, and you feed it fake objects for its real outputs. Then basically you have a pattern like this:

    MockInput -> RealObject -> MockOutput

    So, you set a given input, it runs through a real object, and then you examine your output object to ensure the output was as you expected. Our goal is to test every testable object in this isolated way, and also to test each system end-to-end.

    As an example, an isolated test might be that when we call combatant.takeDamage(5), and our combatant has 4 health, that the combatant actually notifies another object that it has died so we can react to that. Which looks like:

    UnitTest -> Combatant -> MockDeathCallback

    Unit test registers the combatant with the mock death callback, calls takeDamage, combatant does its thing, then the unit test checks the callback to ensure that it was run. If it was, then we know our combatant will notify whoever we register when they die.

    An end-to-end test might have us create a Battle object, add two AI combatants followed by one human combatant, and then update it for the amount of time we expect those two AI turns to take. (The test, of course, doesn't need to wait, it just acts like the time has pased). Then, we ensure it's the human's turn. This tests not only start battle, but battle's update, the AI's update and their start/end turn methods, and the general turn cycle itself.

    We test functionality with isolated tests, and we test application with end-to-end tests.

    So, knowing this, how are we going to deal with not being able to make new MonoBehaviours?

    The question is, what does MonoBehaviour get us? Primarily three things. It gets us a position in world space via transform, it gets us physics (which in this game will be important for 2D raycasting), and it gets us an update many times per second. We can't act on those yet, but let's remember that when we boil it down that's what we're missing. Anything else our object does can be abstracted into the logical layer.

  • RendRend Registered User regular
    PART 2: Let's actually start now maybe?

    RULE 1 OF FIGHT CLUB TDD: You must NEVER write code until you have a failing test which requires that code to pass.
    (There are two exceptions: Code which is untestable, such as MonoBehaviours, and code which does not have anything to test. For instance, you would not test a public variable in a class.)

    So let's write a test. When I begin I usually like to start by defining the environment, which to me means the board. I know that I want my game to have terrain like obstacles, difficult terrain, etc, which means each of the spaces in the board are going to need to store some data. As such, my architecture is basically that Board is a collection of BoardSpaces. I'll call the pieces I place BoardEntities. So, Board will end up knowing all BoardEntities and the BoardSpaces they're on. BoardSpace will NOT know about Board, neither will BoardEntity. I don't see a good reason for them to, and for either of them to know about Board would cause circular dependency, which we want do avoid.

    The first test I want to write is to add a BoardEntity to a BoardSpace.

    So, the general form of a test is to establish an initial condition, execute the test's content, and then assert that the state of the obejcts is as you'd expect. Here's our test:
    [TestClass]
    public class TestBoardSpace{
    	[TestMethod]
    	public void boardEntitySetsAndRetrieves(){
    		BoardSpace space = new BoardSpace();
    		BoardEntity entity = new BoardEntity();
    
    		Assert.IsNull(space.getEntity());
    		space.setEntity(entity);
    		Assert.AreEqual(entity, space.getEntity());
    	}
    }
    

    Let's think about what we've done here. We create a space and an entity, assert that the space starts with no entity, add the entity to the space, then assert that it's been successfully added. You may be thinking to yourself that this is a toy example, but this is literally one of the first tests I wrote. When I was actually working this.

    We do have a major question to ask: Should outside objects be able to get the entity from a space? The reason we ask this is because if we don't want random objects to be able to examine and mess with it, we should not allow this to happen.

    RULE 2 of TDD: You must NEVER write production code that is useful only for testing.

    In other words, if something doesn't need to be made public, we must come up with a design which allows us to examine and confirm the state without making it public. In this case, we're good, because I think asking the space about its occupant is going to be a core part of the game logic, and indeed, I think that one of BoardSpace's core jobs is going to be keeping track of and producing its occupant.

    So, ok the test is fine. But, it doesn't compile. So, we add:
    public class BoardSpace{
    	public void setEntity(BoardEntity entity){
    
    	}
    
    	public BoardEntity getEntity(){
    		return null;
    	}
    }
    
    public class BoardEntity{
    	
    }
    

    It compiles! Now, I hear you whispering in my ear, "Why don't we just implement those methods? They're dead simple. It's impossible to mess those up." You're right, they are. But that brings us to

    RULE 3 of TDD: Your test must ALWAYS fail before it passes.

    If you never fail your test, you don't actually know if it's doing anything. In fact, if you write a test to "make sure" existing functionality covers a case you think it may or may not, and it passes the first time you run it, you should go into your code and change it temporarily in a way which breaks your new test to ensure it's red. A test is never fully green until it has been red before.

    So, we compile, run the test, and it's red. The second assertion fails, which we want. Now, we correct the issue.
    public class BoardSpace{
    	private BoardEntity entity;
    	public void setEntity(BoardEntity entity){
    		this.entity = entity;
    	}
    
    	public BoardEntity getEntity(){
    		return entity;
    	}
    }
    

    And now it's green. And boy does it feel good. I swear, TDD gives you the same euphoria as you get when you finish a project, but every 5m or so. It's amazing.

    So, we've got a cool test, but let's go back to our initial problem. These things don't yet exist in world space, how to we change this, and more importantly, how do we test it?

    Let's write a test that will exercise this.
    [TestClass]
    public class TestBoardEntity{
    	[TestMethod]
    	public void entityMatchesSpacePositionWhenPlaced(){
    		BoardSpace space = new BoardSpace;
    		BoardEntity entity = new BoardEntity();
    		//Place space and entity at different points
    		//Assert that their locations are not equal
    		space.setEntity(entity);
    		//Assert that their locations now match
    		Assert.Fail();
    	}	
    }
    

    I put the fail there at the end so that it's red until we fix it. It kind of helps me focus to have a failing test.

    Anyway, so, we need the ability to set and get the space of these things in the world. So, we're going to define an interface called WorldTransform:
    public interface WorldTransform{
    	void setPosition(Vector3 position);
    	Vector3 getPosition();
    }
    

    This presents a difficulty. How do we ensure their locations are or are not matching if we cannot look at them?

  • RendRend Registered User regular
    edited March 2016
    PART 3: Mock Objects, or How I Learned To Stop Worrying And Love Abstraction

    Enter the Mock Object!

    Since we can't write code without a test, here we go:
    [TestClass]
    public class TestMockWorldTransform{
    	[TestMethod]
    	public class canRetrieveSetVector(){
    		MockWorldTransform mockWorldTransform = new MockWorldTransform();
    		Vector3 positionVector = new Vector3(1, 2, 3);
    		mockWorldTransform.setPosition(positionVector);
    		Assert.areEqual(positionVector, mockWorldTransform.position);
    		Assert.areEqual(mockWorldTransform.position, mockWorldTransform.getPosition());
    	}
    }
    

    Great, now we make it compile:
    public class MockWorldTransform : WorldTransform {
    	public void setPosition(Vector3 position){
    
    	}
    
    	public Vector3 getPosition(){
    
    	}
    }
    

    ..Test is red. Now we make it green:
    public class MockWorldTransform : WorldTransform {
    	public Vector3 position;
    	public void setPosition(Vector3 position){
    		this.position = position;
    	}
    
    	public Vector3 getPosition(){
    		return position;
    	}
    }
    

    Got it. Alright, let's return to the previous test with our new mock object.
    [TestClass]
    public class TestBoardEntity{
    	[TestMethod]
    	public void entityMatchesSpacePositionWhenPlaced(){
    		BoardSpace space = new BoardSpace;
    		BoardEntity entity = new BoardEntity();
    		MockWorldTransform spaceTransform = new MockWorldTransform();
    		MockWorldTransform entityTransform = new MockWorldTransform();
    		space.setWorldTransform(spaceTransform);
    		entity.setWorldTransform(entityTransform);
    
    		spaceTransform.position = new Vector3(1, 2, 3);
    		entityTransform.position = new Vector3(4, 5, 6);
    		Assert.AreNotEqual(spaceTransform.position, entityTransform.position);
    
    		space.setEntity(entity);
    
    		Assert.AreEqual(spaceTransform.position, entityTransform.position);
    	}	
    }
    
    (Note that we are not testing setWorldTransform because we are not planning on being able to getWorldTransform at all. We still have test coverage of it from this test, but we are not testing it by itself)

    So, we've given each of our objects a transform so it can exist in world space, and asserted equivalence by making that transform a test-only object that exists in our test project only. We probably won't ever need to ask these objects to report their position from the outside, so we can't just add BoardEntity.getPosition(), but by feeding this to our real objects, we make previously un-examinable properties examinable. So, let's make it compile:
    public class BoardSpace{
    	private BoardEntity entity;
    	public void setEntity(BoardEntity entity){
    		this.entity = entity;
    	}
    
    	public BoardEntity getEntity(){
    		return entity;
    	}
    
    	public void setWorldTransform(WorldTransform transform){
    
    	}
    }
    
    public class BoardEntity{
    	public void setWorldTransform(WorldTransform transform){
    
    	}
    }
    

    ...Test is red. Let's make it green:
    public class BoardSpace{
    	private BoardEntity entity;
    	private WorldTransform transform;
    	public void setEntity(BoardEntity entity){
    		this.entity = entity;
    		//entity.setPosition(transform.getPosition());
    	}
    
    	public BoardEntity getEntity(){
    		return entity;
    	}
    
    	public void setWorldTransform(WorldTransform transform){
    		this.transform = transform;
    	}
    }
    
    public class BoardEntity{
    	private WorldTransform transform;
    	public void setWorldTransform(WorldTransform transform){
    		this.transform = transform;
    	}
    }
    

    ...Whoops, it looks like we need an extra method for BoardEntity here. We need a way to set BoardEntity's position from outside. Let's write a test for it.
    [TestMethod]
    public void setPositionChangesWorldTransformPosition(){
    	BoardEntity entity = new BoardEntity();
    	MockWorldTransform worldTransform = new MockWorldTransform();
    	entity.setWorldTransform(worldTransform);
    
    	Vector3 positionVector = new Vector3(7, 8, 9);
    	entity.setPosition(positionVector);
    	Assert.AreEqual(positionVector, worldTransform.position);
    }
    

    Cool, make this compile:
    public class BoardEntity{
    	private WorldTransform transform;
    	public void setWorldTransform(WorldTransform transform){
    		this.transform = transform;
    	}
    
    	public void setPosition(Vector3 position){
    
    	}
    }
    

    ...Red. Now make it green:
    public class BoardEntity{
    	private WorldTransform transform;
    	public void setWorldTransform(WorldTransform transform){
    		this.transform = transform;
    	}
    
    	public void setPosition(Vector3 position){
    		transform.setPosition(position);
    	}
    }
    

    Awesome. Now, our previous test is still red, so we make THAT one green:
    public class BoardSpace{
    	private BoardEntity entity;
    	private WorldTransform transform;
    	public void setEntity(BoardEntity entity){
    		this.entity = entity;
    		entity.setPosition(transform.getPosition());
    	}
    
    	public BoardEntity getEntity(){
    		return entity;
    	}
    
    	public void setWorldTransform(WorldTransform transform){
    		this.transform = transform;
    	}
    }
    

    And blam, there we go! Now when we place a piece on a space, they will match their positions together and the entity will snap to the space.

    Later, we'll have a class called BoardEntityBehaviour which will inherit from worldtransform AND monobehaviour and look basically like this:
    public class BoardEntityBehaviour : MonoBehaviour, WorldTransform {
    	private BoardEntity entity;
    
    	void Start(){
    		entity = new BoardEntity();
    		entity.setWorldTransform(this);
    	}
    
    	public void setPosition(Vector3 position){
    		transform.position = position;
    	}
    
    	public Vector3 getPosition(){
    		return transform.position;
    	}
    }
    

    At that point, boardentity will now exist in the game world, but all of its logic will be executable outside of the monobehaviour, in a land that can be thoroughly tested by automated unit and end-to-end tests.

    This was really long and I actually have a lot more to say about it but I'll stop because holy god this is really long. I really hope this was helpful, and if you want me to keep going I totally will.

    Rend on
  • beecgbeecg Registered User regular
    I really hope this was helpful, and if you want me to keep going I totally will.
    Yes and yes please!

  • Jubal77Jubal77 Registered User regular
    The Humble Bundle is a crap ton of Cryengine assets btw guys.

Sign In or Register to comment.