Jump to content
  • Advertisement

Brain

GDNet+
  • Content Count

    2612
  • Joined

  • Last visited

  • Days Won

    7

Blog Entries posted by Brain

  1. Brain
    Hi Everyone!
    In preperation for the release of Mr Boom's Firework Factory, i have created a release trailer which has been uploaded to various storefronts and to YouTube:
    The release trailer is an incremental improvement on the previous three trailers, one created in 2016, another created in January 2019 and the other in April 2019.
    This trailer was created in Lightworks Pro, using a whole load of video footage of existing gameplay, some stock footage, and static images made in GIMP with green-screen effects to overlay them onto moving images. If anyone's interested in the technical production of this trailer (as i've seen nothing technical relating to creation of trailers on this site) please let me know in the comments below, however please note that I'm generally quite new to video editing myself, certainly no expert, and this may be "a case of the blind leading the blind".
    This new trailer adds the following:
    I've used some of the voice acting I'm using in game, most notably at the end when the name of the game pops up, Mr Boom tells you to hurry up. I thought this would be a good addition as the main character of the game addresses the viewer directly, accusing them of sitting on their ass, basically goading them to go to the steam page. I've added some extra level sequences, mainly level 22, the server room. I'm keeping pretty tight-lipped on new levels now, so that if you want to find out all about later levels, you'll just have to play the game and experience them! At the very end of the trailer, the sequence goes to a VHS style 'noise', and then to some footage of the streamer NorkDorf playing the game. He got extremely wound up by the difficulty at first, and shouting angrily at the game, scared his pet cat out of the room. Every person i've shown this footage to laughed, so i decided to insert it at the end of the trailer, with his permission of course. Within the trailer, i've added YouTube cards which encourage clickthroughs to the store page, basically calls to action. As always feedback is welcome in the comments below
  2. Brain
    Hi Everyone,
    Now that I've returned from Casual Connect London 2019, I can finally progress with pushing Mr Boom's Firework Factory to release. Casual Connect was extremely productive, and an extremely useful resource for getting the game polished and put in front of many eyes. There is a gallery for the photos of Casual Connect below:
    The main things that happened were:
    Over 70 people played the game, with overwhelmingly positive feedback. The game is as hard as we'd intended, but I need to review the tutorial and usability to ensure people are getting stuck in a good way with the game's difficulty curve, and not with the controls and learning what they actually need to do I managed to grow my professional network, meeting many other indie developers One of the people I met has encouraged me to get proper voice acting done for Mr Boom, so the main 'antagonist' of the game will have a voice! What worked?
    Actively encouraging people to come and play the game worked very well. Where most people patiently sat and waited for people to come and play their game, I was not content with this, and would stride with purpose into the walkway and try to convince people that they needed to play my game. A casual introduction of "hi, how are you?" would start a conversation which usually ended with 15 minutes of play. Having a rolling video of the game helped massively, if one person was playing, or we were busy networking, a second person could simply watch the video. Similarly, if someone did not want to play the game, but wanted to know how it worked they could simply watch the rolling gameplay video. Recording various players on my phone as they played help two ways; firstly, it allowed us to analyse their body language and expressions afterwards to determine where they were pleased, happy, frustrated, or confused. Secondly, these may be useful later in footage to promote the game, at key moments where people cheer to themselves or curse the game as they lose or win a level. Having Trello on our mobile phones allowed us to quickly make a list of observations in the background as people played. The appeal of the game was very strong, many people played until they had other places to be (e.g. they had a set of games they wanted to try out, or had to go to a talk or meeting) with the average game session lasting 15-20 minutes. I purposefully chose to take equipment to casual connect which could not edit the code and perform fixes. This forced me to ensure that the game was stable enough on the equipment to not need hotfixes at the event. We didn't want to be the ones sat trying to edit our code at the event for all to see. Reaching out to previous winners gave a whole wealth of useful advice, which we used. Thanks very much to Robert Kujawa at Neurodio for his in-depth email responses which helped tailor my approach to the conference. They produced a guide, which is now available to everyone, based on the emails they sent me. What didn't work?
    The tutorial was not strong enough to stand alone at the conference, Craig and I ended up becoming an interactive tutorial, with each player, explaining the controls and goals repeatedly. This soon became tiresome, but at the same time afforded the opportunity for the player to ask questions as they played. The hardware we had planned to use was not up to the task - the netbook I had ready to stream 1080P video was not up to the challenge on the day, luckily we had backup hardware. Recording people on our phones did not yield the excitement we had hoped to capture. While people got very excited to complete a level that had challenged them for ten minutes or more, they would react naturally and excitedly when we weren't recording, but if they were aware of the mobile phone recording their reactions would change, perhaps due to being uncomfortable with being recorded. What did I learn?
    This may be directly related to the types of professionals at the event, but we were repeatedly asked when this would be available on mobile. Due to this, I intend to look into a mobile port soon after the steam release, either porting it myself or finding a third party able to port it, such as a publisher. Once you put a game in front of real people, outside your testing team, they will uncover bugs you never dreamed possible. Amongst the positive feedback were at least two crashes to desktop, two different forms of lockups, and some weird non-game-breaking visual bugs. For the non-game-breaking bugs, usually the player did not notice as they were too deep into the gameplay and concentrating on finishing the level. The game needs more visual cues. For example ghosted crates on the exit, and above the machines, to give hints to what is going to happen next and what is expected of you:
     
     
     
     
     
    I am now starting on the list of feedback obtained from Casual Connect, so there should be another blog entry of these fixes soon. Stay tuned!
    If anyone has any comments or feedback as always please do leave comments below!
  3. Brain
    I'm really happy to announce that my indie game Mr Boom's Firework Factory has been selected as an Indie Prize finalist and I will be attending
    Casual Connect London 2019 on 28th-30th May where I will be exhibiting my game in the QEII Centre on the third floor.
    Come along and say hi, or have a go at my game in person!
    There will be a live stream during the event.
    I look forwards to seeing you there!


  4. Brain
    Hi everyone!
    It's been a good couple of weeks since I last posted a blog entry. With the help of @Rutin and his 3D model making expertise, i've now implemented the hydraulic press mechanics within the game, and also finished the appearance of the laser tripwires. These were required to implement level 12 of Mr Boom's Firework Factory, the first level to use electricity mechanics.
    The Completed Laser Tripwire
    Starting with the laser tripwires, i selected some free to use models, and some cheap paid models and kitbashed them to end up with something visually appealing, with some help from @Rutin converting 3D studio files into FBX. Using a CCTV camera model, an SLR camera tripod, and some weird electrical gadget, this was the end result, connecting the set using a UE4 Cable Component:

    There are two arrow components in this model, the one in the lens of the camera dictates where the laser beam originates from, and in which direction, and the one on the control box is a connection point for the cable actor which connects it to the object it powers. With this complete, i could move on to the hydraulic press itself.
    Hydraulic Press Development
    For the actual hydraulic press that connects to this laser tripwire, I'd started out with a basic primitive prototype, made of some textured boxes which i sent to @Rutin for a sense of scale so he could get started on something better:

    I then left @Rutin to do his thing, and after lots of to-ing and fro-ing, talking about how it needed to look, he finally came back with the finished product, which i turned into an actor within the game proper...
    The Hydraulic Press - The final model

    Animated Parts
    I also decided to add some extra parts to the model to allow for connection to a cable (the pylon at the top of the model) and to allow for animation of the display screen. The display screen uses a simple texture atlas to animate its text when it is powered up:

    The end result looks like this, and has some emissive applied to make it look more like an oldschool green phospor screen. It only appears when the machine is powered on:

    Hydraulic Press Blueprint Events
    This needed to be connected to the existing power system by deriving the class from my "Powered Object" class, which implements the base interface, and a set of related events added, one for powering it up and one for powering it down, plus a timer to animate the model.
    Power on event
    The power on event is called when the device receives power, it changes the materials of the screen and power button to 'lit' ones with texture atlases and emissives.

    Power off event
    The power off event reverses the behaviour of the power on event, reverting the glowing textures and texture atlases back to plain boring non-animated and non-emissive ones.

    Animation event
    The animation event is powered by two timelines, one to quickly descend the crusher to the ground, detecting any collisions with crates as it sweeps and registers collisions. Any crates it collides with are set to explode as soon as possible.
    The second timeline runs eight times slower, and ascends the crusher back to maximum height. Each of these timelines has a sound attached after it has finished executing, one for a loud slamming sound, and one for a hissing of hydraulics and steam engines.
    Both of these events only operate while the object is powered, and if power is removed, the animation always completes, leaving the crusher part at its highest position allowing crates to pass through.

    Once these are all rigged up, the whole thing can be tied together, which produces some wonderful and destructive machines:


    Videos!
    The videos below show the hydraulic press in all its glory. Note that the first video does not show the camera shake effect, as I had de-posessed the player pawn to reposition the camera in a better position. It appears that in UE4, a de-posessed free floating camera does not receive camera shakes.
    The view from the other side of the machine better demonstrates the camera shake effects used to add polish to the machine:
     
    As always, feedback is more than welcome! Please feel free to comment below, and let me know if i missed anything! 🙂
  5. Brain
    Hi all!
    As I progress through adding yet more levels to my game, I have decided that next on my to-do list is the ability to turn on and off devices around the levels based upon the crates passing along the conveyor belts. The system should give visual feedback as to what is connected to what, without me needing to manually connect cable actors to things when creating level mechanics. I took inspiration from the way sensors work on conveyor belts when building settlements and manufacturing systems in Fallout 4;

    So how would I go about making a similar system? Without further ado, let us begin!
    Part 1: The Sensor
    I first needed to create a laser sensor object that could detect crates passing through it. A quick google found a tutorial on making a 'laser trip mine', as seen in many FPS games, so I decided to use this as a starting point. The difference between this and a mine though is that once triggered a mine will instantly explode, so there is no need to visually display the beam being stopped by an object. In this case, this is essential as well as the ability to detect the type of object which has intersected it.
    I started out by creating a simple red beam particle. This is extra bright, with lots of bloom for gameplay reasons. Although a dimmer, shimmering beam much like a red laser pen or barcode reader would be much more realistic, it is also much harder to see from 'gods eye view';

    We can then use this beam in an actor. I created an actor which could serve as a beam emitter, which for now is just a placeholder cube with an unrelated brightly coloured texture on it. The beam particle emits from its right hand edge in a straight horizontal line.
    To determine how far this beam should go, we calculate its maximum length in the Begin Play blueprint event, and also every time the Tick event fires to compensate for anything in the path of the beam which stops it. The first of these images is the BeginPlay event, which does three things:
    1) Find the actor that the switch powers on/off, identified by a tag
    2) Call the function to line trace the beam to its endpoint
    3) Create and set up a cable actor connecting this sensor to its powered object for visual polish

    The function Create Beam is simple, as it simply gets the start and end locations, spawns an emitter at the start location and sets the end point of the particle effect:
    The Calculate Beam Length is a little more complex, and is called every tick (as we need to adjust the length based on what we find intersecting it using a line trace):

    You'll note that this function does not just return an end position vector. As well as the ending position, it will return a reference to a Crate if one intersects the beam, or an invalid (null) reference if the beam does not intersect a crate. We use this later on to determine whether or not to toggle power to connected actors:

    The three options at the end of this event are implemented by the NativePowered class, which I will discuss in the next section...
    Part 2: Powered Actors
    To make this work, we need a way to represent which actors are powered by electrical connections. I decided to do this as a simple linked list of powered objects which share one common power source. In my game the actual source of the power is not visible, or relavent to the game mechanics, so i don't show a wall socket or similar, to avoid confusing the player. After all, they aren't building these power networks themselves!
    All electricity originates from a switch, so in this case the sensor we implemented above. In the tick event, when a crate intersects the laser, this fires the Set Power State method of the class, which sets the power state of that object, calls the Power On or Power Off event for that object, and then propagates the power change to the next object in the linked list.
    All objects which can receive power derive from one type of Actor, written as a C++ class:
    NativePowered.h
    /* Represents an actor which may receive power from a switch, and be daisy chained to other powered actors. * If a powered actor is daisy chained to another actor, power state is shared between all connected objects. */ UCLASS() class FWF_API ANativePowered : public AActor { GENERATED_BODY() /* Current power state of this actor, on or off */ bool PowerState; /* Next actor in the chain, or NULL if this is the end of the chain */ class ANativePowered* Next; /* Cable component encapsulated within actor, connecting this actor * to the next actor, if there is a chain. */ class APowerCable* Cable; public: // Sets default values for this actor's properties ANativePowered(const FObjectInitializer& ObjectInitializer); // Set power state to on or off, also changes the power state // of any 'Next' actors connected to this one UFUNCTION(BlueprintCallable, Category = "Power|Power State") void SetPowerState(bool Power); // Get current power state of the actor UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Power|Power State") bool GetPowerState(); // Called when the game starts or when spawned virtual void BeginPlay() override; // Called when the actor is destroyed virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override; // Called every frame virtual void Tick( float DeltaSeconds ) override; // Can be overridden to get notification events when the object receives power. // Use this to turn on animations, enable lights, etc. UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Power|Power Event") void PowerOn(); // Can be overriden to get notification events when the object loses power. // You can use this for example to stop animations and disable lights. UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Power|Power Event") void PowerOff(); // Called after components are initialised void PostInitializeComponents(); // The main mesh of the powered actor. As objects that receive power are visible // in the level and have power cables going into and out of them, they must have a // visual presence so a mesh is the default base object. UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Power|Mesh") UStaticMeshComponent* Mesh; // A tag value to find in the next connected actor in a chain. This is then used // in BeginPlay() to locate and link to the next connection. We use tags here because // there is no other way to represent this loose relation in the editor, which // only allows selection of actors that are immediate descendents of the level, // not actors embedded in child actor components, etc. UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Power|Power Connection") FString NextActorInGrid; }; NativePowered.cpp
    /* Powered actors default to powered off */ ANativePowered::ANativePowered(const FObjectInitializer& ObjectInitializer) : PowerState(false), Next(nullptr), Cable(nullptr) { PrimaryActorTick.bCanEverTick = true; /* Create static mesh component */ Mesh = ObjectInitializer.CreateDefaultSubobject<UStaticMeshComponent>(this, TEXT("Mesh")); Mesh->AttachToComponent(GetRootComponent(), FAttachmentTransformRules(EAttachmentRule::KeepRelative, EAttachmentRule::KeepRelative, EAttachmentRule::KeepRelative, false)); } void ANativePowered::PostInitializeComponents() { Super::PostInitializeComponents(); } void ANativePowered::EndPlay(const EEndPlayReason::Type EndPlayReason) { /* Don't leave spawned cables hanging around (literally hanging... lol) */ if (Cable != nullptr) { Cable->Destroy(); } } // Called when the game starts or when spawned void ANativePowered::BeginPlay() { Super::BeginPlay(); /* Is this powered actor daisy chained to another powered actor? */ if (NextActorInGrid != TEXT("")) { /* Find the actor with this tag. Note that if there are copies of the level actor hanging around, * there may be multiple, so always pick the last. */ TArray<AActor*> TaggedActors; UGameplayStatics::GetAllActorsWithTag(GetWorld(), FName(*this->NextActorInGrid), TaggedActors); /* We also can't have the actual NextActorInGrid property be FName directly, because FName values * in properties of a child actor are wiped to 'None' on object initialisation. Bummer. */ /* Did we find any? */ if (TaggedActors.Max()) { /* Set linked list ptr */ Next = Cast<ANativePowered>(TaggedActors.Last()); /* Get the world position of the 'Plug' socket/bone in the source actor. * Enforce a scale and rotation, regardless of the scale and rotation of the actor itself and its components. * If we don't the cable actor flies off in some random direction based on the rotation and scale. */ FTransform nextlocation = Mesh->GetSocketTransform(FName("Plug")); nextlocation.SetScale3D(FVector(1, 1, 1)); nextlocation.SetRotation(FQuat(FRotator(0, 0, 0))); FActorSpawnParameters params; params.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; /* NOTE: APowerCable is basically just like ACableActor. It contains nothing but a UCableComponent, and a couple of * lines in the constructor to set its colour and thickness. * I would just be using UCableActor, but due to a BUG, i cant include it because it won't let me spawn it across * module boundaries due to a link error. Clearing intermediate files did not fix this bug, as the answerhub said it should. * UGH!!! */ Cable = GetWorld()->SpawnActor<APowerCable>(APowerCable::StaticClass(), nextlocation, params); /* HACK HACK - To attach to an actor, we must first specify the actor reference in OtherActor, and then we MUST * (NOTE: MUST!!!!) specify a local offset in the EndLocation, from that actor position in actor transform space. * This is the last line here, that sets the relative offset to that of the 'Plug' socket/bone within the static mesh. * Note that this behaviour is quite poorly documented. */ FComponentReference ref; ref.OtherActor = Next; Cable->CableComponent->AttachEndTo = ref; Cable->CableComponent->EndLocation = Mesh->GetSocketTransform(FName("Plug"), RTS_Actor).GetLocation(); /*FVector(0, 0, 30);*/ } } } // Called every frame void ANativePowered::Tick( float DeltaTime ) { Super::Tick( DeltaTime ); } void ANativePowered::SetPowerState(bool Power) { this->PowerState = Power; /* Trigger overridable events for this actor */ if (this->PowerState) { PowerOn(); } else { PowerOff(); } if (Next) { /* Cascade the change to other attached actors */ Next->SetPowerState(Power); } } /* Simple getter for power state */ bool ANativePowered::GetPowerState() { return PowerState; } /* Base class, empty implementation */ void ANativePowered::PowerOn_Implementation() { } /* Base class, empty implementation */ void ANativePowered::PowerOff_Implementation() { } As you can see here, we determine the next 'link' in our chain of powered objects by using the tags of the actor. We must use tags, rather than just an object reference or pointer, because which objects we can pick using the editor in this way is highly restricted, we can only choose pointers to objects that are direct instantiated objects at the root of the level itself, not for example a spawned actor that is spawned at runtime, or a child actor within a child actor component.
    To get around this restriction we set up our actors in the editor to work with the tags and the code written above:
    Source Object

    Destination Object

    We can repeat this process as many times as we like, within the editor or even at runtime, to create complex chains of powered objects triggered by one switch.
    Adding Sockets
    All that's needed now is to add a socket to a mesh. In this instance i've picked a random large mesh from my collection of props. We add a socket to it called 'Plug', this is where the cables that link each item visually connect them:

    Part 3: The complete system
    Now we can test and view the complete system as a whole. As you can see, the system visually connects the related objects, as well as having the logical association behind the scenes. Passing a crate through the laser will toggle the connected objects. This will be used in level 12 to power some quite awesome hardware, namely a large hydraulic press that will destroy crates unless you toggle some switches in the right order to turn it off!
    The system below is a simple test system, featuring two powered objects and a sensor:
    ##
    As always, comments and feedback are more than welcome! Please vent your spleens below! 🙂
  6. Brain
    Hi everyone!
    Another week, another update to the development blog for Mr Boom's Firework Factory!
    The Easy Stuff: Creating the Stock Room
    This week, I have been creating a way to reward players for successfully completing levels. Every time a level is completed with a three star rating (the best possible rating) the game will route the player to the stock room on the completion of the level. The stock room is an enclosed area, containing some junk and six boxes. Selecting from the boxes will reward the player with one of the following:
    A random power-up Nothing (along with a condescending message) A booby prize, for example a burned cereal box. This new feature can be seen working in the video below:
    The booby prizes, if won, will just stack to an inventory, e.g. multiples are represented by "x2", "x3" etc. These can't be gotten rid of an have absolutely no use except cosmetic, so I can imagine that the player may find a way to meta-game this, and i may add an achievement relating to this if there is time before release.
    It is obvious on the video above that selecting boxes in the stock room is simple enough if the player is using a mouse and keyboard, simply clicking on a box will select it, and this is done effectively using the onclick events within blueprint on those particular components for the crates.
    The More Interesting Stuff: Making This Work With A Gamepad
    This becomes slightly more complex when dealing with a gamepad. Unreal Engine 4 has facilities when dealing with UMG for UI so that each control which can receive focus has a 'next' control for each direction, so if for example "Up" is pressed when focused on control A, it can be told that control B is "Up" in relation to it, and that control "B" should then receive focus.
    When dealing with an actor built with components, which is not UI, we're out of luck and have to implement this ourselves. Time to roll up our sleeves and see how I did this!
    Firstly, we must map out visually how each of the crates relate to each other.
    Consider that each crate has an index number between zero and five, and that for each crate index, we have a table of relations based on the direction of travel for which is "nearest" to it in that direction:

    So, for example, if the current crate was crate index 1 and we wanted to move up, then the crate index would become 0. Similarly if the crate index was 0 and we wanted to move right, the crate index would become 2. We can represent the currently selected crate then as a simple integer value.
    All clear on this? OK, let's continue!
    Based on this layout, we can click on each of the components in the actor, and set up these representations using the tags within the component, always sticking to a uniform layout, tag index 0 representing down, index 1 representing up, index 2 representing left, and index 3 representing right:

    Once all these have been defined, we have a way, completely within data, and without having to adjust code, of being able to indicate the relationship between the components so we can navigate between them. All we need to do now is write some code to do it.
    Firstly, we need a function which if given a a direction (as an integer) will update the current crate to a new crate, still going with the same constant directions as above, 0 = down, 1 = up, 2 = left, 3 = right. We'll call this function "Move Selection Cursor", because... well why not?:

    We will also need to define in the constructor, or in the Begin Play Event, the indices of each of the crates in a simple array (as a member variable of the actor class) which this function can use. Remember the order of the items in this array is important as we determined our layout in the first picture when we mapped out the relationships between the objects. If there were more objects or in different positions in relation to each other, this would need to be adjusted:

    Once we have done this, it is a simple matter to attach five events to action mappings, which will allow us to detect input events from the gamepad, and route them to the functions we have created. For readability's sake, we can also define four constants (see the top left in the green square) representing our index values for up, down, left, and right respectively, which means that the code becomes more understandable, rather than just a mass of magic numbers between 0 and 3:

    Once this is in place, as many before me have said, sometimes in jest and sometimes in seriousness, "it just works".
    I hope you found this journey into the depths of my mind enlightening, please do comment and leave feedback if you have any questions or comments!
    Stay tuned for more blog entries!
     
  7. Brain
    Hi everyone,
    It's been two weeks since my last blog post, and many things have happened in the development of Mr Boom's Firework Factory.
    The R&D Department
    The game now has a fifth level (not including the tutorial level mentioned below) which is set in the factory's R&D department. The R&D department is a rather dodgy area, very much below board, dealing with radioactive waste (for use in fireworks??? You be the judge!) and toxic gases. As you can tell from the products in use, this place has taken a turn for the worst and isn't what it seemd to be when the player started out on their journey:

    The poison gas crates emit a huge cloud of green gas when they explode, which causes the player to lose vision of the game temporarily. If done at the wrong moment, this can be catastrophic:

    The radioactive goop crates were previously covered in a past blog post and leave goop stains all over the level, which are destructive to anything that passes over them.
    Getting the game on Steam
    Next, and most importantly, I have finally set up the store front on Steam, and configured my build process to push builds to steam via Steampipe. This took a little time to get working, and during this time I established a closed beta. Under the offer of free keys for the game, and credit in the game's credits page, many people contacted me asking to take part, and based on the feedback they gave I now have a reasonable sized bug list and feature list on the project's Trello.

    The most commonly requested of these was a tutorial, to explain the controls and the basic aim of the game. Each user had to have the basic controls explained by myself (there was no readme, or online help) and the basic aim of the game, as it seems that the game is sufficiently different from most of the puzzle games available on steam as to confuse many new players.
    Without further ado, this leads on to the next item on this blog post...
    Creating a tutorial level for fun and profit!
    To be effective and useful, the tutorial level in my game had to have the following qualities:
    Be very easy to complete Allow for multiple different choices of input method (e.g. keyboard, gamepad) Have obvious points where gameplay could pause and pop-up hints can occur Still be possible to fail, to try out failure and see what happens Not be easy to break the tutorial flow Be possible to complete the tutorial in under a couple of minutes I decided to settle on a simple level, with the fewest possible number of usable junctions that can be placed whilst still allowing a crate to bounce  between them in a loop (two), and the fewest number of machines (one). The level would work by popping up a dialog box indicating how machines worked, and how to move the camera, then emitting one crate from the machine, and after it had emitted the crate, popping up another dialog box indicating how to rotate crates. Once this dialog box had popped up the ability to rotate junctions became available, until this point the player can't rotate the junctions to help prevent breakage of the 'scripted' events.
    As soon as the player has rotated the correct junction to the correct angle, the next scripted event will trigger, popping up a dialog box to confirm that this was the correct action. There is a short delay of a couple of seconds before the final dialog pops up to explain how crates wait for the grab claw, and then the level ends.
    There were two verisons of this tutorial, which only varied based on the text content of the speech bubbles; one for keyboard/mouse usage and one for the gamepad, with different summaries of the buttons to use.
    If played efficiently, this takes about one and a half minutes. As far as tutorials go, this is pretty much short and sweet.
    First attempt (AKA: How to scare off new players)
    The first iteration of the tutorial level had dialog boxes which just contained raw text:
    This did work, somewhat, but it had two flaws which required a second iteration be done, these flaws were:
    Each dialog box was a wall of text, off-putting to the new player and hard to digest Aligning the dialog text with the rounded speech bubble and having it work on any resolution of screen was proving to be near impossible Tutorial Trek - The Next Iteration
    The next version of the tutorial involved me spending a good couple of hours in GIMP polishing images. Instead of using raw walls of text that are hard to align with a speech bubble and still look good, i decided to draw the speech bubbles as PNG images:

    I ended up drawing six of these, two of each of the first two speech bubbles that were controller specific, and two of the last two generic bubbles.
    I drew each one with some particular features in mind to make them better than the wall-of-text equivalents:
    Each speech bubble was to make more effective use of the rounded bubble area Where a bubble mentioned controller or keyboard controls it would use the expected iconography to indicate what to press Names of controls, or important gameplay elements would be highlighted in red, e.g. crate, machine, left stick. Actual images of gameplay elements would appear in the bubbles to help the player relate (this way the player knows what a machine looks like as they saw one in a tutorial bubble, most real life factory machines are not square boxes with an exhaust sticking out of them!). These images would sometimes overlap the edge of the bubble by design, to draw attention to them. Most importantly: All wordage was to be shortened as much as possible! I then rolled them out into the game:

    And that, as the say, is all he wrote.
    Feedback and comments are as always very welcome!
  8. Brain
    Hi all,
    The main project for this weekend was to get powerups tied to an actual level in the game, which i have now got working as in the video below:
    With this out of the way I have decided to progress onto the next feature of the game which requires development. This is the level select page, which you are presented with if you have previously unlocked any levels in the game, letting you revisit and replay them to get better scores and collect extra powerups.
    Within the menu, each level should have a preview image, or thumbnail, showing its appearance to allow for visual memory of which level is which. This was previously implemented on the .NET version of Firework Factory, as can be seen below:

    In the newer unreal engine version this must be re-implemented. In the previous version, screenshots were taken by spawning the entire level, taking a screen capture of the level and storing that in a texture.
    I decided to do something similar in UE4, only to find that doing so would be overkill, as instantiating a level is quite expensive, and would have to be done dozens of times on startup just to capture screenshots to render targets, which could then be turned into materials, and displayed in the menu. Instead, i chose to make a 'photo studio', and get the levels 'photographed'.
    Yes, you heard right, a photo studio.
    The solution for the problem is to first create a separate map. Within this separate map create a couple of strong directional lights to ensure that there isn't too much shadow in the images. Secondly, create a floor made of a non-emissive completely black material with no reflectiveness. Essentially, this floor is impossibly black, much like it has been painted with vantablack.
    Next, I set up a blueprint to incrementally load each level in a loop, wait 1 second for the postprocessing effects to settle, and issue the console command:
    HighResSnapshot 3.0 The code for this can be seen in the blueprint below:

    This causes the game to save a screenshot of the current level to the folder Saved\Screenshots\Windows, where it can be loaded into GIMP and autocropped, giving me an image like the one below, which is the one for level 4:

    These can then be associated with the levels as a Texture2D by importing them into the content folder, so that a thumbnail can be displayed. As i add new levels, i just re-run the process to generate the snapshots, pick the new levels images, autocrop them and drop them into content, which takes all of 30 seconds. This means i can ensure that all the images are taken from the same angle, with the same lighting, the same postprocessing effects, and have an image of exactly the same dimensions.
    Next on the list: Use these thumbnails for an actual level select menu!
    Feedback as always is more than welcome, stay tuned for further updates!
  9. Brain
    Hi all,

    It's been quite some time since i last posted a journal entry. This hasn't been for lack of development of the game, more lack of free time where i'm not being a Dad, working my day job, or finding a few minutes to add to the game. Unfortunately for everyone who reads my journals, writing about my progress kind of came last, and for that i'm sorry.

    There have been many advancements to the game in the past three months, these are:

    Porting of most of the gameplay code to C++

    Certain parts of the game were quite slow. I had narrowed this down through a small profiling session to loops within Blueprints. It seems that while Blueprint is good for some things, for loops especially it has some issues, as well as casts, which cannot be resolved at compile-time like many C++ casts can, and will quickly bog down performance.
    To resolve this i rewrote large amounts of the code in C++. During this time, I encountered some annoying bugs, such as this one, which I reported to the Unreal Engine developers, and they are fixing as we speak:



    To rewrite my code in C++ i had to basically create a C++ base class for each blueprint and move variables first, then code a function at a time, manually rewriting it. In the process of doing this I learned a lot about Unreal Engine that i didn't know before, and this was a great learning exercise for me as well as helping improve the game's performance.

    Adding new crate types

    I have finally had time to add a new crate type to the game. The new crate type contains radioactive waste, and i have dubbed it the "goop crate". This crate appears later on in the game, after a major plot twist. The crate explodes with radioactive blobs of glowing green muck upon impact, which linger around and cause damage to any crates that pass across them:



    I created this particle effect using CPU particles with collisions, which when they collide with a horizontal surface (by checking the surface normal) they leave a decal on the surface, which is embedded within an Actor with a timer set to fade it out by adjusting values in its material instance.

    I am quite happy with the effect, and hope to find some time to add some more types of crate very soon!

    Adjusting the apperance of the machines

    I have decided that as i cannot get the previous artist for the game on board, and the look and feel of the game has changed anyway, i would like to go with a darker, grittier and less comedic, less cartoony feel for the game. The first part of this is to replace the comedy machines that produce the crates with something slightly more realistic. To aid this i have placed grilles on the sides of the machines and created a more realistic lamp which sits atop the machine. When the machine is due to produce a crate, a siren sounds and the lamp flashes. The flashing is realistic, working with a spotlight pointed at a mirrored surface, and a rotating lamp body which has a great effect. The model for the lamp cost me a cool $15 on turbosquid (let's be honest here, i am a programmer and i cant model something like this to save my life). Below is an example of the new machines in the game, and a development test in the editor:




    As always comments are more than welcome! (it's encouraging to know that people read and sometimes enjoy what i write here). Let me know your thoughts, minor flames, or whatever comes to mind.

    Bye for now and stay tuned for future developments!
  10. Brain
    It's been a very long time since any new blog posts have been posted about my current game project.
    I have decided that my new year's resolution for 2019 will be to get my game finished and out there within this year; After all, it's almost at the point now where an MVP or Vertical Slice exists.
    The claw has finally been fixed, and redone with a bit of kitbashing to make it nicer looking:

    Over the past year where there have been few blog posts, life has taken over and taken away a lot of the free time i had for gamedev. However over Christmas i have managed to get a bit more done again, and have moved onto where i left off, which was power-ups.
    The game will have several power-ups, which are gained by successfully completing levels with some finesse, and can then be used to make finishing some levels easier, or in some cases possible at all - the first level which introduces power-ups cannot be completed without them.
    Power-ups derive from a simple C++ class:
    UCLASS() class FWF_API ANativePowerup : public AActor { GENERATED_BODY() public: UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Powerup") bool AllowDropOnMachines; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Powerup") bool AllowDropOnCrates; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Powerup") bool AllowDropOnJunctions; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Powerup") bool AllowDropOnConveyors; // Sets default values for this actor's properties ANativePowerup(); // Called when the game starts or when spawned virtual void BeginPlay() override; // Called every frame virtual void Tick( float DeltaSeconds ) override; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Powerup") UStaticMeshComponent* Mesh; UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Powerup") UMaterial* GetSprite(); UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Powerup") UAudioComponent* OnUseAudio; UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Powerup") void Pickup(const FVector &PickupLocation); UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Powerup") void Use(const FVector &DropLocation); UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Powerup") void UseOnMachine(class ANativeMachine *machine); UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Powerup") void UseOnJunction(class ANativeJunction *junction); UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Powerup") void UseOnCrate(class ANativeCrate *crate); UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Powerup") void UseOnConveyor(class ANativeGridActor *conveyor); UFUNCTION(BlueprintCallable, BlueprintNativeEvent, BlueprintPure, Category = "Powerup") FString GetName(); }; Each of the boolean values at the top of the class indicate what a power-up may be dropped onto, each of which will have a different effect - some positive, and some negative. These booleans will allow the game to present a different (affirmative) cursor for elements that the power-up may be used on at all, to give the player some guidance and feedback.
    The first such power-up is the wrench:

    This power-up can be dropped on either a machine, or a crate. If it is dropped on a machine, it disables that machine for ten seconds, during which time it will not produce crates. Any crates it would have produced will be moved to the back of the production queue and produced later. This may cause other machines to produce crates in different orders.
    If you accidentally (or even on purpose) drop a wrench onto a crate, the crate will immediately explode.
    If you continually use wrenches on a machine, each successive use damages the machine slightly much like sending a crate back through the same machine, eventually causing it to explode.
    When the power-ups are dropped onto a supported object, the "UseOn*" methods, e.g. UseOnMachine() are called, where the object the power-up was dropped on is passed.
    There is much more to come, after a few power-ups have been implemented (next on my list is glue, which causes crates to stick to the conveyor, and oil, which makes crates go faster, but is highly flammable!) I will be moving on to saving of the game state to a file, and selection of levels from a list of levels that have already been completed, via the main menu.
    Once this is completed, I will be much closer to actually completing the vertical slice of the game which has been in progress for over a year now.
    Feedback is as always more than welcome!

  11. Brain
    Hi all,

    Another day, another feature! The last couple of days have kept me busy creating a "physics powered" grab arm for picking up the crates. When the player gets the crates to the exit, the claw comes to get it. This is as creepy looking as it sounds. I decided to build the claw from primitive static mesh types, and bind the meshes together with physics constraints so that they acted in a hinged way, using forces and impulses to manipulate the arms.

    The reasoning behind this is so that when an object is within the claws, the physics library (in UE4's case, PhysX) can automatically clamp the four arms onto the box, or whatever else is there, and lift it. Once the item is lifted it can let go at any point and let it drop (which it will do if you give it the wrong crate). The other approach to this would involve a skeletal mesh, rigging, and inverse kinematics. That set of options is a non-starter for me because I am extremely lousy at Blender, not even able to create something with simple shapes, never mind rig or animate a model, and secondly, in my opinion, inverse kinematics in unreal engine 4 is a bit awkward to implement and last i tried it, a bit unstable causing random crashes.

    It took me a couple of hours to create a model i was happy with, using static meshes and some material work, and then another hour or so to fine tune the physics of the hinges on the arms, giving me a rather disturbing looking grab claw:











    The claw can be controlled by two simple physics radial forces, one to draw the arms together into the centre, restricted by a maximum angle, and one to push the arms wide apart ready to pick something up (this is required so that they don't clip through the thing theyre trying to grab, and cause them to go completely nuts).

    These actions are shown in the video below:






    Finally, the proof is in the seeing, here is the claw in action, with some really cool new explosion effects!






    Comments and feedback as always are more than welcome!
  12. Brain
    Hi everyone!

    It has been a long time (nearly a year) since i last worked on this game. My usual development routine involves having multiple games in progress at any one time. Once i start feeling burn-out coming on, or boredom setting in with one project i flip to the other, until one or the other is complete, always having more than one ready to be developed.

    This is the way it has been for me for over a decade, and it works very well for me.

    To this end when restarting working on this project i took a long hard look at what I could and couldn't quickly improve in the codebase. The graphics of the game were severely lacking, with no real lighting, no shadow mapping, no physically-based rendering, none of the textures had normal maps, etc.

    To add these features myself and get them looking good would take quite some time. Once they were added, i would need to implement a toolset to manipulate them correctly, and then test it all. This would also take a long time.

    My other project, which some of you may have been following, was written in UE4, and this gave me the features mentioned above out of the box. I decided therefore to take my existing C++ code, and adapt it to Unreal Engine 4, using UE4 to do the rendering and sound (this also gets rid of the licensing requirements of using fmod for sound and music, as these features plus 3D audio are built into UE4).

    It took surprisingly little time to get something looking pretty good out of UE4 using my existing assets, scaled up using GIMP's Sinc (Lanczos 3) scaling which prevents blockiness you usually get when scaling up an image. In the end, i plan to re-create these assets myself using edge detection to turn them into line art, then scaling the line art to suit. Currently, my old 64x64 textures have been scaled successfully to 512x512.

    By being very creative, and spending the better part of 6 hours tweaking light levels, placing spot and point lights, emissive materials etc, i came up with something that pleases my eyes and looks very impressive, especially compared to the old game:

    [table]
    [tr][td][sharedmedia=gallery:images:7291][/td][td][sharedmedia=gallery:images:6404][/td][/tr]
    [tr][td]New Graphics[/td][td]Old Graphics[/td][/tr]
    [/table]



    The graphics in the new version are a bit of a hack. To keep performance good when lots of particle effects will be going off, i have intended to keep FPS high at this point, and consider carefully everything i do. Therefore, the lighting must be carefully planned out. A lot of the lighting must be dynamic (movable lights) rather than static, baked lighting, as most of the items inside the room can move, including the 'play table' itself. To this end i decided that even though the game's room looks like it is indoors on a sunny day, there is actually no real sky light and no real outside environment.

    Outside the room is a quad, which has an image of an outdoor scene textured onto it. The material used to texture this quad has a high emissive value (texel colour * 8.0) which makes it shine with brightness like a bright sunny day when viewed from inside a dark room. This in itself is not enough to cause light shafts to stream in through the open windows however and shadows to be cast on inside items, as the emissive materials in UE4 are a bit of a hack, which to cut a long story short, technically don't really emit light (unless you're using light propogation volumes, which are a performance hit, and still experimental even in 4.10) - it is just a post-processing effect or two.

    So, to emit light, i have added a single spotlight slightly above, in front of, and to the left of the textured quad, still outside the window. The intensity of this light has been ramped up to 100,000 units, which causes a really bright light to be shone in through the window with much the same intensity as the sun on a bright summer day. For all intents and purposes, the effect is the same as being inside a room with a bright day outside. To complete the effect, as all factories and offices seem to have them, a point light has been placed just below the ceiling of the room, casting a less powerful set of shadows almost straight down. This is why when you look at the image the machines etc seem to have two shadows, one darker than the other.

    The complete layout of my room (as i can't just screenshot the editor, there is no "light outside" to be cast on it to show you) is sketched below:

    [sharedmedia=gallery:images:7292]

    I am still undecided wether or not the high bloom effect actually contributes to the scene or detracts from it, comments on this as always or anything else are welcome :)
  13. Brain
    Hi everyone!

    It has been a busy month or so in the land of game development for me. My game has moved on to now having a proper menu system, a loading screen and asynchronous streaming levels. Along with this I have also added a mini-map which uses a second camera directly above the player's skeletal mesh, which follows its position, always 25 metres above the player's head and never rotating, so the mini-map always faces north. The stamina bar has been moved below the mini-map and made smaller. I decided to generally ape the way GTA V does this, as it is likely that players are going to be more accepting of something similar to an existing system, and a UI is a UI, expectations are generally king and doing novel things just tends to pee people off.

    Alongside the development of the game, i have spent some time redoing the map of the game world. I have decided that the map will not be 100% accurate, just like most maps in medieval times, it will have flaws. Most of these flaws will be subtle. You can see the new map at the top of this article, to see the old map, you can find it on the website of the web based version of the game in all it's ugly glory.

    The menu system of the game is fully 3D, using a seperate level. The game's level structure has been arranged so that the persistent level contains nothing but what is required. Essentially this means it contains nothing but a recast navmesh and a navigation bounds volume. There are two sub-levels which are streamed in and out when required, the first one being the menu map, and the second being the core of the game world proper. Within the game world, various regions are sub-levels of this too, and may be loaded and unloaded via streaming as and when required to save memory.

    When the main persistent level loads, it's "On Begin Play" event simply streams in the menu level. While any level is being streamed in, a UMG full screen widget is drawn on the screen which consists of the loading page. The loading page has an animated progress bar/marquee on it to show the player that the game is actually doing something, and hasn't crashed. The menu and game world, in their own "On Begin Play" events hide this UMG widget, revealing the map proper.




    It might also not be noticable but i have also turned off "Temporal Anti-Aliasing", a default feature of unreal engine 4 which puts motion blur on everything. For most uses this just makes text above the NPCs heads unreadable and blurs the detail on textures. I have disabled this, replacing it with the more reliable FXAA scheme.

    This now actually looks like a proper game, as it doesn't just drop you straight into the game world. There are of course still holes and incomplete bits - clicking "load saved game" does nothing, and dying now fades to black with a message "*** TODO: Reload last save ***". It will be a while yet before this message goes away as save game files are low down on my list of priorities at the moment, next on my list being invetory systems and conversation trees.

    For inventory systems i plan to have a simple TList within each thing which may possess items, for example the player, NPCs and treasure chests etc. In the right situation, objects derived from an AInventoryItem actor class may be moved in and out of these lists. Basically, a corpse will be treated as a storage locker It sounds grim, but in my mind, this makes a lot of sense from a program perspective.

    As always feedback and comments are welcome below, please let me know if you have any thoughts that might help with my game going forwards
  14. Brain
    Hi everyone!

    This weeks simple project was to create an effective enemy spawner in UE4 using C++. I already had a simplistic blueprint version of this, but it had some bugs which were causing crashes in the engine and editor. Once blueprint scripts start crashing the editor, it is hard to troubleshoot the actual cause of the crash, so in these instances i always find C++ easier to debug, having the vast wealth of tools available that are visual studio 2013.

    The concept of an enemy spawner is simple. When the player character enters a box defined within the level, one or more enemies spawn at a set location which may or may not be within the confines of that box.

    I have decided to share the majority of this class with the community, as i found very little complete code on how to do this, and researching the correct syntax for the SpawnActor() method alone took me a good thirty minutes of googling.

    To implement the NPC Spawner, i started out by simply deriving a class from ATriggerBox:
    UCLASS()class SEVENSPELLS_API ANPCSpawner : public ATriggerBox{ GENERATED_BODY() bool triggered; /* List of character types to spawn at or near the Spawn Location */ UPROPERTY(EditAnywhere, Category = "Spawner") TArray> CharactersToSpawn; /* Spawn location. If not set, no enemies spawn. */ UPROPERTY(EditAnywhere, Category = "Spawner") class ATargetPoint* SpawnLocation; /* Number of copies of each type of character to spawn */ UPROPERTY(EditAnywhere, Category = "Spawner") int32 Count; /* This value indicates the maximum number of times the spawner will trigger enemies. If zero, the default of 1 is assumed. */ UPROPERTY(EditAnywhere, Category = "Spawner") int32 MaxTriggers; ANPCSpawner(); UFUNCTION() void OnBeginOverlap(AActor* Other, UPrimitiveComponent* Box, int32 OtherBodyIndex, bool bFromSweep, const FHitResult &hitResult); UFUNCTION() void OnEndOverlap(class AActor * OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);};

    The class attaches to two delegates for OnActorBeginOverlap and OnActorEndOverlap. These notify our class when any other actor walks into the edges of our trigger box, and are initialised when the object is constructed:
    ANPCSpawner::ANPCSpawner() : triggered(false){ GetCollisionComponent()->OnComponentBeginOverlap.AddDynamic(this, &ANPCSpawner::OnBeginOverlap); GetCollisionComponent()->OnComponentEndOverlap.AddDynamic(this, &ANPCSpawner::OnEndOverlap); if (!MaxTriggers) MaxTriggers = 1;}

    Within these two delegate functions, we use the boolean value 'triggered' to ensure that the event may only occur once (for some reason, UE4 sends the begin overlap notification twice for the player character, followed by the end overlap twice), and that we only trigger the event when the player character walks into the trigger area (otherwise, the NPC which is spawned may also create another NPC within the spawn box, and so on, until the system runs out of RAM and crashes!)

    Once we have verified that we are triggering in the right situation, we use the SpawnActor method of the UWorld class to spawn the relevant actor objects:
    void ANPCSpawner::OnBeginOverlap(AActor* Other, UPrimitiveComponent* Box, int32 OtherBodyIndex, bool bFromSweep, const FHitResult &hitResult){ APlayerCharacter* pc = Cast(Other); if (pc && !triggered && MaxTriggers > 0) { triggered = true; /* Player overlapped the NPC Spawner, spawn the enemies */ FActorSpawnParameters parameters; parameters.Owner = this; parameters.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn; for (int i = 0; i actor : CharactersToSpawn) { logprint(actor->GetClass()->GetName()); if (SpawnLocation) { GetWorld()->SpawnActor(actor, SpawnLocation->GetTransform(), parameters); } } } MaxTriggers--; }}void ANPCSpawner::OnEndOverlap(class AActor * OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex){ APlayerCharacter* pc = Cast(OtherActor); if (pc && triggered) { triggered = false; }}

    This serves as a great way to test combat, by putting a known trigger box at some point near the player's start point, walking into it to spawn the enemy we want to test:




    As always, comments and suggestions are more than welcome!
  15. Brain
    Hi everyone!

    So it's been a while since i last posted a blog entry. It has been an extremely busy time for me, tweaking and improving the performance and design of my game. I have finally moved on from using Blueprint for parts of my game to using C++, where i feel much more at home and it feels more like "proper" programming.

    Part of this has been the rewrite of my AI controller, which manages all enemy types which attack the player. The Blueprint version had become a complete spaghetti mess, partly due to the fact that unreal is still new territory to me, and secondly due to the fact that the language is supposed to look a bit like spaghetti to start with, nodes all over the screen connected by linked 'wires'.

    I settled upon a finite state machine as my AI implementation. There is a decision tree system already built into unreal engine 4, but implementing my AI using this means treading water in the "GUI-designed-program-code" pool again, so i decided that it would be easier and more readable just to use C++ for it.

    This new AI controller is designed to tie in with the new sword-and-shield combat techniques that are now part of the game, as the player is able to dodge, block, and jump as well as backpeddle:




    The AI controller has several states:enum AggressiveState{ SSOD_AGGRESSIVE_STATE_PATROL, SSOD_AGGRESSIVE_STATE_RANDOM_WALK, SSOD_AGGRESSIVE_STATE_FLEEING, SSOD_AGGRESSIVE_STATE_BACKOFF, SSOD_AGGRESSIVE_STATE_FOUND_PLAYER, SSOD_AGGRESSIVE_STATE_GETTING_CLOSER_FOR_ATTACK, SSOD_AGGRESSIVE_STATE_ATTACKING, SSOD_AGGRESSIVE_STATE_BLOCKING, SSOD_AGGRESSIVE_STATE_DODGING};
    The AI starts out in one of the first two states "Patrol" or "Random Walk" depending on if it has a set of patrol waypoints set up in its properties or not. If it has a set of waypoints (a simple array of ATargetPoint actors) then it will walk around these waypoints in a loop without stopping. If it does not have a set of patrol waypoints, instead it simply walks to random navigable locations within a defined patrol radius:void AGenericAggressiveAI_v2::BeginMoveToNextPatrolPoint(){ if (me) { ATargetPoint* nextwaypoint = me->Waypoints[PatrolIndex++]; MoveToLocation(nextwaypoint->GetActorLocation(), 30.f); if (PatrolIndex == me->Waypoints.Num()) PatrolIndex = 0; }} void AGenericAggressiveAI_v2::BeginMoveToLocation(){ if (me) { MoveToLocation(UNavigationSystem::GetRandomPointInNavigableRadius(this, HomeLocation, me->PatrolRadius), 30.f); }}
    The AI character has three movement speeds, starting off in its "Walk" speed (1.0 times the Maximum Walk Rate), then "Jog" speed (1.8 times the maximum walk rate) and finally the "Run" state (2.5 times the maximum walk rate). The AI can decide to walk, jog, or run depending upon what it is doing. Dodging attacks is done at the running speed, whilst trying to get close to the player is done at jogging speed. Patrolling and random movement is always done at normal walking speed.

    For simple communication with the rest of the game, there are several delegated events attached to the AI controller:UPROPERTY(BlueprintAssignable) FBeginAttackingPlayerDelegate BeginAttackingPlayer; UPROPERTY(BlueprintAssignable) FStopAttackingPlayerDelegate StopAttackingPlayer; UPROPERTY(BlueprintAssignable) FTriggerAggroDelegate TriggerAggro; UPROPERTY(BlueprintCallable) FPlayerIsAttackingDelegate PlayerIsAttacking;
    The BeginAttackingPlayer is called by the AI controller when we are close enough to launch a melee attack against the player. This happens once, then once the attack is complete the AI controller once again tries to get close to the player for another attack, repeating the process until itself or the player is dead.

    The TriggerAggro event is called when the AI has spotted the player for the first time. It triggers the aggro sound and animation (character specific) in the character class.

    The PlayerIsAttacking event is called by the blueprint portion of the code when the player tries to attack an AI combatant. This is picked up by the AI, and if there is any immediate risk to the AI character (e.g. they are close enough to the player to be harmed) then the AI character will try to backpeddle out of reach of the player, then regroup to try again:void AGenericAggressiveAI_v2::BeginBackOff(){ if (me) { FVector Direction = me->GetActorLocation() - TrackedCharacter->GetActorLocation(); FVector BackOffPosition = TrackedCharacter->GetActorLocation() + 2.5f * Direction; state = SSOD_AGGRESSIVE_STATE_BACKOFF; MoveToLocation(BackOffPosition, 30.f); }} void AGenericAggressiveAI_v2::OnPlayerAttacking(ACharacter* TrackedCharacter){ /* We receive this delegate when the player is executing an attack. * If we are close enough to the player that this might hurt us, * attempt to dodge it - backpeddle immediately. */ if (DistanceFromTrackedPlayer() GetCapsuleComponent()->GetScaledCapsuleRadius() * 20.0f) { me->GetCharacterMovement()->bOrientRotationToMovement = false; me->GetCharacterMovement()->bUseControllerDesiredRotation = true; Run(); StopMovement(); GetWorld()->GetTimerManager().SetTimer(MoveDelay, this, &AGenericAggressiveAI_v2::BeginBackOff, 0.1, false); }}
    This provides for a level of intelligence which seems to be quite smart to the player, even though the base rules behind it are quite simple. Compared to the Blueprint based AI, the reactions are smoother, smarter, and more aggressive. Playing against an AI enemy, the results speak for themselves in this video:



    Comments and feedback as always are welcome below
  16. Brain
    Hi everyone,

    The game is now advancing at quite some pace as i find all sorts of things here and there to add, all of which are requirements for gameplay. The main one of these this week has been weapon equip facilities.

    The concept of carrying weapons and using them is fundamental to most role playing games. In The Seven Spells Of Destruction, you can carry a limited number of weapons restricted by the size of your backpack and your level (higher level characters find more ingenious ways to stash more items in the same space and have the capability to carry it all).

    The first part of this is being able to equip weapons.

    [table][tr][td][sharedmedia=gallery:images:6491][/td][td]The character i picked to be my lead hero (which i selected from OpenGameArt) has an axe permenantly attached to his right arm as a secondary element of the skeletal mesh. To work around this and allow selection of other weapons, without altering the model and effectively making my life harder as a non-artist, i simply changed the mesh's material to a transparent one and removed its collision. In short, the player now carries an invisible axe which he swings round to no effect.

    To replace the invisible axe i used the construction blueprint for my player character to instantiate the sword (or whatever other weapon he is using) and attach it by the handle to the player's right hand. Due to some animation issues in the model, i have some other blueprint monitoring this to ensure the player never looks like he's stabbing himself.[/td][/tr][/table]

    As part of the weapon spawning, i have a scaling factor which specifies the size of the weapon. The use of this is for later in the game's development where a 'grow weapon' spell might be cast making the weapon the player is carrying temporarily (and rediculously) larger.

    [table][tr][td][sharedmedia=gallery:images:6495][/td][td]The end effect is a character which hefts a longsword, single handedly too a shown in the GIF on the left.

    Along with development of the character and his weapon swinging behaviour, i have added more blueprint-driven systems to the world, such as the ones below:[/td][/tr][/table]

    [table][tr][td][sharedmedia=gallery:images:6492][/td][td][sharedmedia=gallery:images:6493][/td][td][sharedmedia=gallery:images:6494][/td][td][sharedmedia=gallery:images:6496][/td][/tr][/table]

    Comments and suggestions are welcome below
  17. Brain
    Hi everyone!

    As the next step in my game i have decided to anger various right-wing animal rights groups by testing out virtual weapons on virtual chickens. The next step in my game was obvious in that some form of combat was needed.

    I have implemented a combat system based on the approximate swing arc of the sword (or whatever weapon the player is weilding) which deals a set amount of damage per swipe, for now. Each chicken or any other NPC listens to the "On Take Any Damage" event which allows it to be hit.

    [sharedmedia=gallery:images:6509]

    By default, chickens wander in a 100 unit radius of their original spawn location, unless injured, which resets their 'spawn' location and centres their wanderings on a new location. Hitting them also puts them into panic mode which can make them move faster and more often.

    [sharedmedia=gallery:images:6511]

    Alive chickens emit random clucking sounds every few seconds, and when killed a death animation is played for the static mesh and a death sound played at the same time.

    It is rather hard to hit the chickens, as can be seen in the video below:




    Questions or comments as always are welcome below!
  18. Brain
    Hi everyone,

    I have had some spare time to spend on getting the AI working in my game for enemies. Moving on from simple Chickens, which don't do anything much except walk around waiting to be killed, I have now moved on to getting smarter and meaner creatures to stalk you through the map and make the player's life more akward.

    I started out by obtaining two nice models to use as my first two beasties (click the pictures below to enlarge):


    [table][tr][td]
    [sharedmedia=gallery:images:6533]
    [/td][td]
    [sharedmedia=gallery:images:6514]
    [/td][/tr][/table]

    These two beasties all inherit from the same 'creature' framework I have created known as "GenericAggressive". The GenericAggressive type is built of two parts, a blueprint AI class, and a blueprint controller class. The controller class is responsible for selecting the correct visual and audio behaviours for the creature, for example the sounds it makes when it sees the player, the animations it uses for spotting, attacking, and chasing the player, and the sounds it makes when it is hurt. These are exposed via parameters to the instance of the object:



    The AI class controls the movement behaviour of the creature, e.g. when it is patrolling, when it is chasing the player, when it gives up and how aggressive it is (click the image below to enlarge):



    The AI has a few simple "dumb" traits which combine together to give the impression of a much smarter whole:
    All GenericAggressive creatures have a default home location, which is set in the constructor of the blueprint to whever it is placed initially in the map.
    The creature will patrol at random around its home location, picking random navigable points within that radius to walk to, with a random break of one to three seconds before it picks another route.
    Each type of creature has a sight cone, provided by the "AISensing" object, and an auditory sense. Detection of the player by them passing into the sight cone causes the monster to lock onto the player's coordinates and start moving quickly to that location. Simply hearing the player causes the creature to navigate to where it heard the sound, in the hope that during this route it will see the player and be able to chase them proper.
    A creature which is chasing the player is then 'locked on' to the player, and will pursue them to within 150% of its patrol radius before giving up, or until the player or the creature are dead.
    When a creature locks on to the player, a 'sensed player' animation and sound are played. For example for the cave troll, it roars and raises its weapons in the air ready to charge. These sense animations and sounds serve as warnings to the player of imminent attack.
    Any sounds generated by the creature can only be heard within 1000 units of the creature, with the sound volume dropping off linearly along that range.
    A creature which is locked onto the player will use the navmesh to attempt to get into range, repeatedly re-calculating its route until it is considered to be within weapons range (at present all creatures have non-projectile weapons such as swords or stingers). Once in weapons range, the creature will attempt to strike if it is within a 90 degree arc facing the player, dealing damage if the player is within the collision capsule of the creature.
    Any creature which is close enough to wound the player is also close enough to be wounded by the player and the victor is simply the one who lands the most hits whilst dodging the hits of the other. Each creature can have any number of attack animations, each of which deal different damage amounts and can have any number of attack sounds.
    If the player kills the creature, its AI controller is detatched to stop it functioning, and a 'death' animation and sound are played (creature-specific). For example, the scorpion curls into a ball and the cave troll falls backwards and drops its swords.
    If the creature kills the player, the player controller is detatched from the player to prevent input to the player character having any effect, the death animation and sound for the player character are played and the level blueprint runs a matinee sequence to fade the screen to black, causing the game to end. Any creatures close by to the player when the player dies will switch to a celebration animation, for example the cave troll waves its swords in the air and cheers. I am hoping to find some creatures that have feeding animations, as this would be a fitting end for any adventure unfortunate enough to die at the claws of those creatures...
    If a player manages to get more than 150% of the creatures patrol range away from the creature (calculated as vector length) then the creature simply gives up, and walks back to its spawning point, starting its patrol sequence again. At any point if it sees the player again, it re-initialises the attack sequence.
    If there are creatures alive within 1000 units of the player character, the player character switches to his 'nervous' idle animation, which causes him to look around at random and keep his sword close to his chest. At all other times the normal 'idle' animation is used.

    Combined together, these rules make for some pretty interesting gameplay, as shown in the animated gif below (which shows me getting my ass handed to me on a plate by my own game... how embarrasing!):


    [size=2]A high resolution YouTube version of this, with sound, can be found here.


    Ok, i admit it... I purposefully died to demonstrate the health bar and fade to black sequences. (honest!)...

    On a related note, now that the screenshots and videos i am putting out there are showing some real action, and not just random wandering across an empty map, i find myself spending more and more time each day on social media drumming up enthusiasm for the game. This is easier than i anticipated as everyone loves a good gif of a fight, so simply recording videos of combat, converting them to gif and putting them on twitter seems to be going a long way in spreading my name around right now. Time will tell!

    As always, comments, suggestions, questions are all welcome below!
  19. Brain
    Hi everyone!

    As another week passes it's that time again to showcase what new features i've added to the game.

    [table][tr][td]
    [sharedmedia=gallery:images:6502]
    [/td][td]This week i have concentrated on animations, and using the unreal engine per bone blend along with some alpha blend tricks to allow for a programmable animation of the player character. The player character can at any time either be in the 'scared' state or the 'not scared' state, which affects his idle animation. Whilst 'scared', he looks around in a paranoid way. For now, for testing this is bound to the ALT key. When there are hostile enemies added to the game, being within range of hostile enemies will trigger this paranoid behaviour in the character.[/td][/tr][/table]

    [table][tr][td]Left-clicking on the mouse causes the player to swing his sword and attack. This is built from two animations of slashing downwards and across which are blended using a random alpha value, meaning the arms can make a wide variety of diagonal movements. Each slash of the sword generates one of three wooshing sound effects to give the weapon some weight.[/td][td][sharedmedia=gallery:images:6501][/td][/tr][/table]

    The UE4 "blend by bone" facility allows me to bind two animations at once to a skeletal mesh, with one animation effecting certain bones in the skeleton, and another animation effecting the rest of the skeleton. For example, the running animation effects the legs and whilst attacking the attack animations effect the body from the waist upwards by specifying the bone named "Spine".

    I decided to invest a small amount of money in my game and purchased a very nice castle model, which i am using to good effect within one of the larger cities. This can be seen in the image above, and the video below:



    I have also adjusted the speed of the character within its blueprint, so that whilst wading through water the character's travel is significantly slowed, and whilst swinging the sword there is also a (much smaller) slowdown.

    There's still a lot more to do, but progress is being made! Stay tuned for further updates...

    As always, any feedback, comments, and constructive criticisms are welcome below
  20. Brain
    Another day, another post.

    I have now managed to fix the shadows and as part of this have moved on to using full dynamic global illumination. This has allowed me to implement a full day/night cycle.

    I am now progressing with adding towns and cities to the map and further amounts of foliage. The video below shows the sunset, moving through the swamp, and around the wall at the edge of one of the larger cities;



    In the future i plan to have rope walkways across the deeper sections of swamp to save the character having to walk whilst holding his breath (let's be honest here, you can't swim in armour!) and to improve the swamp foliage, replacing the trees with vines and floating detritus.

    Next on my list, importing and creating LOD levels for approximately 100 building models, and associating the correct materials with them...

    Comments welcome
  21. Brain
    Hi everyone!

    This post earmarks the start of my new journal here on gamedev,net. I tend to start multiple projects at once, so that once i start feeling burnout bite on one, i can change to another and keep up some form of momentum without completely burning out trying to finish just the one project.

    To this end, i have decided to rewrite one of my earlier projects, The Seven Spells Of Destruction. This is a role playing game with a very long history which was born in book form when i was thirteen (for reference this is now 21 years ago!).

    The first version of this game started out simply as a 'choose your own adventure' style novel, inspired heavily by Steve Jackson and Ian Livingstone's Fighting Fantasy novels. I have always had the urge to create bigger, and better than what is available (otherwise, really, what is the point in doing it?) so to this end i set out to create an adventure with 1480 locations, the books published and available in the shops having usually around 400 locations including the endings.

    Creation of this work took approximately a year and a half, and somewhere i still have the handwritten copy of this book, which was written in fountain pen during break time and lunch hours at school and is now barely legible as the ink has started to fade after two decades.

    Luckily, long before this was lost forever I purchased my first computer, a BBC Master 128k, and proceeded to type this entire novel into Inter-Word, the only word processor available to me at the time.

    Throughout the years, as i got newer more powerful PCs this document was ported and enhanced as it followed me across systems, from Inter-Word to Word 2.0 for Windows 3.1, and then to Office 2000. By the time i was running Office 2000, i was now at university and was 20.

    It was at this point i sat down and looked at the document, now able to program for quite some years, and decided that this would be better if it was a game. At the time, web based games were in their infancy and all the rage, so i set out to make the book web-based.

    Spending the next year, i created a simple web based engine, which could 'execute' a game-book. To allow this, i split the novel into 1480 files, replacing descriptions of combat/monsters, treasure pickups, paragraph links with an XML-like language. Any tags in the markup were interpeted, updating the character details as required, and anything that was not a tag was simply displayed on the screen. Simple anti-cheat mechanisms were added to prevent teleporting, and i .... promptly shelved this project and got on with others, forgetting all about it until i left uni a couple of years later.



    A couple more years rolled by, by this time i was around 25, and decided to bring this project out of mothballs with the intention of making it publically accessible (the version i had written before had only ever ran on my personal Linux machine) and adding lots of extra content.
    To achieve this i heavily promoted the game on my IRC network and before long had amassed a small team of about 15 GMs, who busied themselves adding text content, new weapons, encounters, areas, and testing the game.

    At its peak, the game had approximately 1500 users and at any time 200-300 of them could be logged in. This was short-lived however as I quickly found that the support time required to support 200-300 active users 24/7 was more than i could offer whilst holding down a paid job, and more than my free volounteer GMs were willing to put in. Basically, i was slowly moving out of the "small game" range and into more "MMO" style territory, without the finances to back it up.

    Due to this, again the project stagnated, although again it was programattically complete, playable from start to finish with around 60 hours of gameplay.

    Fast-forwarding to the present day, and I often look back at this project through rose-tinted glasses and analyse my mistakes and my successes and have decided to resurrect this project for a third incarnation.

    Using unreal engine 4, i intend to re-create this project with a modern engine, and a much more modern look and feel.

    The aim of the project is to get all the game's locations and NPCs and plot playable in as short a time as possible, sacrificing graphics and sound at the moment. To compensate for this i intend to use only creative commons and public domain 3D and sound assets from sites such as opengameart for the time being, which so far has allowed me to sclupt the landscape with quite some ease. Compared to writing my code from scratch in C++ and DirectX 11 as i have for Firework Factory, the rate (and graphical quality) of development is nothing short of impressive.

    I am intending to keep the game pretty much 100% blueprint based, purely for simple portability reasons. If there is no C++ code to be comped to native code, there should be less issues with portability.

    So far, there have been two iterations of the design, as I have left some aspects (such as graphical presentation) rather open ended and fluid. Having an open-ended design has worked quite well at actually producing a finished game in the past two iterations, and having a completed game with a fleshed out world and characters will allow me to quickly create the world in less time.

    The game world is rather large, as shown on the (very badly drawn) map from the web based game, below;



    I have compressed this map significantly in the UE4 version to fit into a sensible timescale and make it easily navigable by the player. Currently it takes approximately eight minutes to walk end to end of the map, without accounting for any obstacles in the way. I intend to make clecver use of obstacles to make the map harder to navigate in a straight line, hopefully making the map seem much larger than it is. Later in the game fast travel might become a possibility via teleportation spells.

    The first screenshot used the Unreal Engine 4 above view template, with point and click to navigate;



    I quickly found however that the models available to me did not look as good from this viewpoint, and not having the resources to draw my own, i looked into alternative viewing angles, settling finally on third person view from behind the character;



    This version of the game now has the rough outline of the world added to it, which can be navigated by the player character, and has several types of environment including a forest, mountains, rivers, a desert, and a swamp. This video demonstrates travel across my somewhat empty world between the rough centre of the map above, above the mountains but below the swamp, across the north bank of the western river Larret then north into the desert, taking a grand total of 4 minutes 37 seconds in straight lines.

    Several things available in the web based version will be dropped from the graphical version for sake of development time and my sanity;

    The web based version had several races and professions of character, each affecting the entire game and ending. This will add years to the development time, so will be dropped.
    The web based version had simple multi player capaibilities such as chat, P2P combat and P2P pickpocketing. This is going to be dropped as the game will once again be single player only.
    Several plot arcs have the ability to re-shape the world (similar to how blowing up Megaton in Fallout 3 levels a town and changes its occupants, the same can happen to a city in the game if you let a certain high level NPC raze it...) This might stay and might not. I am undecided.

    Each of these areas has their own ambient sound effects triggered by an ambient audio object. There are a few issues still which i intend to fix, notably:
    The dynamic lighting setup is broken horribly, causing the character to have no shadow cast. I think i set the brightness of the skylight too bright and set the wrong colours, washing out the shadows.
    The scale of some of the instanced foliage is way out, causing the massive flowers you see in the video.

    Once these are fixed i am moving on to adding villages, towns and cities in the correct places, which i have already found creative commons artwork and models for.

    I am very aware of the ambitiousness of this project - I have completed this project in several forms and each time it took me over a year. I have no doubt that this third iteration might take even longer still, but the reward and achievement would be more than worth the effort.

    Feedback and questions are as always more than welcome!
  22. Brain
    Another week, another feature!

    This week heralds the addition of a loading screen. Partly due to a thread here on gamedev.net i decided that perhaps now was the time to add one, as shown below.




    This actually turned out to be quite a challenge, due to the way i had designed my game.

    At the point where the assets are loaded, the game loop is not yet running, and nearly none of the subsystems are initialised. This makes it harder than it otherwise would be to display and represent a loading screen, and leaves me grappling with a slightly alien environment without all my event driven conveniences.

    I decided to approach this as follows:

    Firstly, I had to adjust my sprite cache, to allow pre-caching specific named items at startup without enumerating the entire directory (which contains hundreds of files) and support for streaming objects on request:
    /** * The SpriteCache class is a container object which loads and stores the Sprite2D * objects, which can be referenced by a string key. SpriteCache is accessed via the * read only [] operator, e.g. sprites["name"]. */class SpriteCache{private: FireworkFactory* creator; std::unordered_map, std::hash> sprites; Sprite2D* Get(ResId id);public: SpriteCache(); SpriteCache(const SpriteCache &copy); SpriteCache(FireworkFactory* creator, const std::wstring &directory); SpriteCache(FireworkFactory* creator, const std::wstring &directory, std::vector preload); void CacheDirectory(const std::wstring &directory); void Stream(const std::wstring &directory, const std::wstring &file); ~SpriteCache(); Sprite2D* operator[] (ResId id); void ReleaseAll();};

    This can then be called when starting up the game to load a select small number of 2D sprites for the loading screen only. The loading screen is made up of several pieces, a bottom part of the bomb, the top part of the bomb (there are various versions of this showing different amounts of fuse remaining), the text saying "Loading..." and the spark, which is alpha blended into the scene. When we initialise the sprite cache, we load only the first "top portion" of the bomb with a complete fuse, and as further sprites are needed they are streamed on request:
    sprites = SpriteCache( this, L"Sprites", { L"bomb-bottom.png", L"bomb-spark.png", L"bomb-top-1.png", L"bomb-top-overlay.png", L"loading.png" } );

    I then created a class called LoadingScreen (more on this later), and i wrapped creation and destruction of the LoadingScreen object around my resource loading calls:
    loader = new LoadingScreen(this, Util::ReadDirectory(L"Textures/*.dds", true).size() // Number of textures to load + Util::ReadDirectory(L"Sprites/*.png", true).size() // Number of sprites to load + Util::ReadDirectory(L"sfx/*.wav", true).size() // Number of sound effects to load ); /* Load assets here... */ delete loader; loader = nullptr;

    The second parameter to LoadingScreen is simply a figure used to calculate how many steps should build the progress meter. For this, we do a total count of all sound effects, sprites, and textures in the game by enumerating the directories, and using only the size of the returned vector in-place.

    The LoadingScreen class looks like this:
    /* Represets the loading screen of the game. * Note that when this is instantiated, the game loop is not yet active. * To draw and update the loading screen, you must call the Draw() or * Increment() method which renders the current frame to the render target. * The loading screen requires only DirectX and Direct2D to be initialised. * Deleting the object triggers the explosion effect. This is will block until * complete. */class LoadingScreen{private: /* Creator object */ FireworkFactory* creator; /* Sprites (pre-loaded by SpriteCache) */ Sprite2D* bomb_bottom; Sprite2D* bomb_spark; Sprite2D* top_overlay; Sprite2D* loadingtext; /* Streamed sprites (loaded on request by SpriteCache) */ std::vector bomb_top; /* Sprite positions */ D2D1_RECT_F bomb_position; D2D1_RECT_F text_position; D2D1_POINT_2F spark_position; /* Number of completed steps */ int done; /* Total number of steps */ int steps; /* Last "top of bomb" sprite. When this changes, * we request a new sprite be streamed from disk. */ int last_top; /* Waypoint coordinates for positions of the spark along the fuse. * There are actually 235 of these, each contains an X and Y * coordinate making 470 values. */ static const double waypoints[470];public: /* Create a loading screen which expects 'steps' increments */ LoadingScreen(FireworkFactory* creator, int steps); /* dtor automatically plays the explosion animation */ ~LoadingScreen(); /* Draw current frame of loading screen at done/steps*100 percent */ void Draw(); /* Add completed steps to the animation */ void Increment(int step);};

    As you can see, there is a Draw() method, and an Increment() method. The Draw() method is called directly by the constructor once everything is set up, and by the Increment() method. Different subsystems call the Increment() method as they load assets, increasing the number of steps completed, and redrawing the screen with the new progress:
    sprites[crc32] = std::make_shared(new Sprite2D(creator->GetDX()->D2D->GetRenderTarget(), *f)); if (creator->GetLoader()) creator->GetLoader()->Increment(+1);

    An important feature of the loading screen is that when its destructor is called, it will play the 'explosion' animation, which blocks for 100 frames while it updates. This then leaves the game in a state ready for the backbuffer to be cleared and the game proper to be launched.

    You might also be asking yourselves "so if no subsystems are properly initialised when the loading screen is being displayed, where do the sounds come from?" - The answer to this is simple. Because we cannot guarantee that the sound system is initialised, or that any sound effect assets are loaded, the simple sound effect for the fuse is embedded into the executable as a win32 custom resource type. We use the WinMM PlaySound() API call to play the embedded wav file:
    PlaySound(TEXT("IDW_FUSE"), GetModuleHandle(NULL), SND_RESOURCE | SND_ASYNC);

    Of course, by the time the explosion is ready to be displayed, we can be sure the sound system and all the sound effects are ready for use, so we stop the PlaySound call, and use FMOD to play the explosion sound:
    /* Stop the fuse sound */ PlaySound(NULL, NULL, SND_FILENAME); creator->GetSFX()->PlayEffect(SFX_LOADING_BOMB);

    That about sums up the trickery and deception that is my loading screen. As always, comments are welcome below :)
  23. Brain
    Over the past month I have busied myself adding lots of tiny bits to the game, some of which are obvious to players and others not.

    I have added a new menu design, with sound effects and animations;

    [sharedmedia=gallery:images:6400]

    I have also added a new logo for the game on the menu screen.

    [sharedmedia=gallery:images:6407]

    I have also sped up and increased the pitch of the boss's speech so now he sounds more like an unintelligable voice on the other end of a cartoon telephone conversation - i think this sounds much nicer, and i have made the text in the speech bubble incrementally appear as the boss speaks. Every syllable of gibberish causes appearance of one new word until all are displayed, or the mouse is clicked to cancel it.

    More importantly, I have also added a clock to the game. The original 2D version had this clock, but it never was finished, when it reached zero, it would do nothing.




    As you can see the clock starts with its 'minute' hand at the 3PM position, and the 'second' hand makes one complete revolution per real-time second. Each revolution of the second hand advances the 'minute' hand to the position of "360 / time limit * elapsed seconds" degrees around the clock face. The clock face is drawn rather simply: clocksprite->Draw(position); float radians = minuteshand * (M_PI / 180.0f); rt->DrawLine(centre, D2D1::Point2F(centre.x + (cos(radians) * handlength), centre.y + (sin(radians) * handlength)), minutes_brush, 8.0f * scale.x); if (running) { radians = (secondshand - 90.0f) * (M_PI / 180.0f); rt->DrawLine(centre, D2D1::Point2F(centre.x + (cos(radians) * handlength), centre.y + (sin(radians) * handlength)), seconds_brush, 3.0f * scale.x); } clockglass->Draw(position); if (HurryUp && running && !creator->IsPaused()) title->Draw("HURRY UP!", titlepos, messageopacity);
    When writing this code i did consider using matricies to translate the positions of the lines used for the hands, but i decided that in this case, good old-school sine and cosine seemed more readable to me, and they work well.

    When at least 70% of the time allowed for the level is taken up, the clock starts to shake (as if under pressure) and a message appears on the screen stating to 'HURRY UP!'. If the player still hasn't finished the level when over 90% of the time is taken up, the clock shakes more violently still, and once all time is taken up, all crates active in the level explode, which can and usually leads to level failure. Level* lev = creator->GetLevel(); D2D1_RECT_F rumble = position; float used = Ticks / lev->TimeLimitSeconds; if (running && !creator->IsPaused() && lev && used > 0.7f) { float offset_x, offset_y; if (used > 0.9f) { offset_x = Util::Rand(-4.99f, 4.99f) * scale.x; offset_y = Util::Rand(-4.99f, 4.99f) * scale.y; } else { offset_x = Util::Rand(-2.99f, 2.99f) * scale.x; offset_y = Util::Rand(-2.99f, 2.99f) * scale.y; } rumble.top += offset_y; rumble.bottom += offset_y; rumble.left += offset_x; rumble.right += offset_x; clocksprite->Draw(rumble); } else clocksprite->Draw(position);
    At each of the incremental changes at 70% of the allowed time limit and 90% of the allowed time limit, the pitch and speed of the game music increases, in a rather comical way. I got the inspiration from this from sonic the hedgehog, on the underwater levels when running out of oxygen. No matter how much i try, i will never forget the frustration of those levels and that tune always sticks in my head. float used = Ticks / lev->TimeLimitSeconds; if (used > 0.9f) { creator->GetSFX()->SetMusicPitch(1.8f); } else if (used > 0.7f) { creator->GetSFX()->SetMusicPitch(1.5f); HurryUp = true; }
    In many levels the addition of this clock will add extra challenge to the game, and was a missing game mechanic i have been waiting until now to implement.

    Next on my to-do list is power ups. I have several of these in mind each of which can be purchased with points acrued from successfully completing levels:
    A glue gun, which makes a piece of conveyor sticky, pasting a box to it which won't move for a while
    A grease gun, which has the opposite effect, accellerating any box to pass over it
    A grab claw, like those seen in amusement arcades, which takes any selected box and deposits it somewhere else of your choosing
    A spanner, which when thrown at a machine disables it for a while and stops it producing crates, changing the order of production

    There is room for more (and i'm open to suggestions in the comments!) but these were the four that i planned to have level designs to exploit.

    Comments as always are welcome!
  24. Brain
    It's been a month or so since i last wrote a journal entry here, but this has not been due to lack of work on the game.

    Rather the opposite in fact, i find myself spending so much time creating the game that i have very little time left in my life to write journal entries! Oh, what a first world problem it is.

    Of many things i have busied myself with over the past month, one of them has been rewriting and optimising how resources are managed within the game.

    I have settled on doing this in a particular way which i thought it worth sharing with the rest of the community.

    Firstly, each resource has an ID key which is called a ResId, and is 32 bits long. This is a strongly typed value:BOOST_STRONG_TYPEDEF(UINT32, ResId);
    This actually creates a class containing just the native type (UINT32) with various operators for assignment, copying and comparison. It ensures that I am forced to use "ResId" as my variable type all through my code and not get lazy and start using UINT32, which means that if i choose to change the size of ResId (for example to 64 bits) at a later date, this can be done relatively painlessly in one place.

    ResId types are calculated at compile time by a pre-build step. The pre-build step is a perl script (attached at the bottom of this journal entry, if you are curious!), which enumerates a set of directories and builds a header file, which is full of definitions like this:/* Compile time hashes for files within the Textures directory. * Automatically generated by Util/makehashes.pl. DO NOT MODIFY * by hand! */#pragma once#define TEX_CONVEYOR (static_cast(0x4eec1561)) /* Textures/conveyor.png */#define TEX_CRATE_BREAKABLE (static_cast(0x64d3a7f5)) /* Textures/crate-breakable.png */#define TEX_CRATE_DYNAMITE (static_cast(0x57f6ebd3)) /* Textures/crate-dynamite.png */#define TEX_CRATE_MORTAR (static_cast(0xb1ddcc8a)) /* Textures/crate-mortar.png */
    As you may have noticed, each of these is a simple CRC32 value of the constant name, e.g. 0x4eeC1561 is a CRC32 of "TEX_CONVEYOR". The script which generates these header files only updates them on disk in the event they need to change, to avoid needless rebuilds of the project dependencies.

    Armed with this metadata which we built at compile time, we are now able to define a texture cache class:/** The TextureCache preloads all textures into SRVs and allows access to them via operator[]. * It may throw a std::runtime_exception when constructed with a directory name and DX11 object, * if it cannot load a texture. All textures are loaded from a virtual directory within the zip * file on PhysicsFS. */class TextureCache{private: /** All resources are stored in an unordered_map which uses a nerfed hash function. * Performing std::hash on ResId always just returns the same ResId, as ResId's are hashed * already at compile time. */ std::unordered_map, std::hash> textures; /** Used internally by operator[] */ ID3D11ShaderResourceView* Get(ResId id); /** Builds a descriptive string with an error text and error number from a windows stringtable * resource, and throws it as a string within a std::runtime_error exception object. */ void ReportError(HRESULT hr, UINT caption_id);public: /** Create empty but initialised object, used when assigning inplace */ TextureCache(); /** Copy constructor, usage of CComPtr above ensures that we don't lose our SRVs * when we copy, so this is mainly just to copy the unordered_map itself. */ TextureCache(const TextureCache &copy); /** Initialise texture cache by reading all textures from a given directory using WIC. * The directory is virtualised via PhysicsFS. The DX11 object contains the context and * device upon which the SRVs are to be associated. */ TextureCache(DX11* dx11, const std::wstring &directory); /** Release all CComPtrs by nulling them */ void ReleaseAll(); ~TextureCache(); /** Look up any given texture resource by ID, e.g: * ID3D11ShaderResourceView* srv = cache[TEX_CRATE_DYNAMITE]; */ ID3D11ShaderResourceView* operator[] (ResId id);};
    You might have noticed something strange about the hash function i am using. I have written my own hash function for "hashing" ResIds, which effectively does nothing. This is because ResIds are hashed at compile time, so any further implicit hashing would be a time wasting exercise:namespace std{ /** This is a nerfed hashing function for resource caches where unordered_map is used. * To prevent the unordered_map doing anything with the Resource Id, we just return it * verbatim here. Otherwise, we can't gaurantee that the hasher for unsigned int won't do * anything to the resource id, wasting time. There's no use hashing a value at runtime * that's already been created as a hashed value at compile time. */ template struct hash { inline size_t operator()(const ResId &v) const { return v; } };};
    We can then construct a texture cache simply and effectively on startup, without any needless heap allocation as follows:textures = TextureCache(dx11, L"Textures/*.png");
    When called, this constructor enumerates the content of the Textures directory, finding all PNG files, and loading them with WIC. Each one it loads, it builds a CRC32 value of the constant name (which it determines from the filename) and stores it in the unordered_map keyed by its ResId:TextureCache::TextureCache(DX11* dx11, const std::wstring &directory){ HRESULT hr; const FileList tex = Util::ReadDirectory(directory, true); for (FileListIter f = tex.begin(); f != tex.end(); f++) { CComPtr LoadedTexture; size_t texturelen = 0; const char* texturedata = Util::ReadWholeFile(*f, texturelen, true); if (texturedata) { hr = DirectX::CreateWICTextureFromMemory(dx11->Device, dx11->Context, (const uint8_t*)texturedata, texturelen, nullptr, &LoadedTexture); if(FAILED(hr)) { ReportError(hr, IDS_ERROR_LOADING_TEXTURE); } else { std::string processed = Util::ProcessFileName(Util::GetFileName(*f), "TEX_"); ResId crc32 = static_cast(Util::CRC32(processed)); DebugOut()
    You might also have noticed that there is no operator[] method used for assignment. This is on purpose, so that if i accidentally ever type "cache[VALUE] = someSRV;" this will be caught as a compile-time error. Such an action is not expected and should be forbidden at this point.

    There are a few gotcha's with this implementation, the most important being hash collisions. It is possible, but unlikely, that I can get hash collisions by using CRC32 to hash the resource IDs. If this does become a problem, I will look to moving to CRC64 or FNV-1a instead. Time will tell if this will prove to be a problem at all.

    A future enhancement would be runtime loading of new textures, and releasing of unused textures to stay within a memory budget. Right now, i have enough memory and small enough number of textures that this isn't required.

    So, enough code. I am sure your first question when looking at this journal entry was "well, who is that lady in the picture at the top of the page?"

    The lady in question is the secret agent character for the game. Half way through the game she replaces the boss at intervals, giving you advice and subtle warnings about your manager's antics. Having enough free time to do so, and wanting to try my hand at drawing once more, i decided to create the concept for this character and start shading it myself.[table][td='33'][/td][td='33'][/td][td='33'][/td][td='33'][/td][td='33'][/td][/table]

    As you can see from the image, i am now about half way through this and all i can say is that creating art takes a lot longer than creating code especially if it doesn't come naturally like it doesn't to me. For me to create reasonable art, i have to spend many tedious hours within GIMP, gently massaging pixels to be where i want them. Time will tell if i am able to create some art passable enough for the game, and of course feedback from anyone more artistically inclinded about my creation is always more than welcome!

    On with the show, and back to the grindstone. Please comment and leave feedback if you have any questions!

    Keep an eye out for my new article, coming to this site soon: How to automate deployment of your game!

  25. Brain
    Hi everyone!

    Another week has passed, and more features have been added into my game. Today, I have found some time to create the end of level animation.

    I have drawn my inspiration heavily from casual mobile games such as angry birds, cut the rope and Bag-It, which all have a sequence of stars at the end of the level which fill depending upon the amount completed.




    I also managed to find some very nice sound effects on opengameart.org which have several different variations, each one becoming more excited and louder the more they progress through the sequence. I have used these to ensure that the better you do the more congratulatory the music seems.

    Of course, if you don't get the required percentage at all, you get the classic 'game over' music, as in the previous 2D version of the game.

    The general concept is to remove the need for the user to interpret percentages - in a short glance of a nice looking animation they have determined if they passed or failed the level and if their next step should be to try again (or perhaps throw their controller at the monitor in frustration maybe?) or advance straight to the next level with a big smile on their face, punching the air in a "YES!" pose.

    I have also completed various small bits of polish such as the "picking list" on the bottom right of the screen, which indicates which crates are coming next and what order is correct. As the crates move along this list, the sprite shakes as if under stress and rumbling sounds are played.

    Dropping crates off the edges of the playing field are now accompanied by a cartoonish "whee-kersplat" sound effect.

    Oddly enough, just these small pieces of polish is making the game seem more like a game as time goes along, and showing non-technical strangers videos of the game in progress now results in "oooh's" and "aah's" as opposed to "what is that? looks like minecraft...".

    On a related note i have decided to call the main character "Eric Boom", or "Mr Boom" for short as a result of this thread...

    Stay tuned for further updates from the firework factory!
  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!