Wednesday 11 May 2016

Final Tweaking and Bug-Fixing, and Post-Mortem

This is my final blog post relating to my Final Project: Creating a Strategy RPG. This will include what tweaks have been made in the final day, and a Post-Mortem of the project.

Tweaks and Bug-Fixes

Terrain Implementation and Final Movement Issue
While sadly, neither of these components were resolved. I attempted to implement randomly generating forest tiles, and during this period it came to my attention that AI units were moving to tiles already taken by other units.

For the forest tiles, I implemented a method into the Grid class:

I realised that the above method would not guarantee that 1/6th of the map would be populated by forest tiles, as it would skip over any tiles that already were forest, and i would continue, the population would still remain dense enough to have an impact, and could potentially be refined later.

During this, as stated above, I discovered AI units moving onto already populated tiles, and decided to prioritize fixing this issue first. I realised this issue would also effect User units. I decided to first focus on removing this option for the user units, and created a method in the GameManager class:


I created a method separate for the User units because:

  • The AI do not have their moving bool set to true until they have already plotted their path.
  • The AI rely on the path target being set to the User unit they are attacking, but while a node is unwalkable, it is not detected properly by the target. 
This method seemed to work for the User units, until further play-testing. When a User unit died, the first if statement of the method started declaring that the argument was out of range. I attempted:
  • Placing the first half of the if statement into the update function, running the method only if true
  • Creating a temporary variable for the current unit
  • Ensuring the next User unit did not have moving set to true
For the AI's side of it, I created two methods; TurnOnBlock, and TurnOffBlock. They were the BlockUnitTiles method split into two, so they could be run at the beginning and end of functions or methods as necessary, but this is where I discovered the issue of unwalkable tiles not allowing the path target to draw a path to it. With User units, the path would select the last tile that was moused over, but as the target goes directly to a units tile when the AI is operating, it simply could not find a target.

I decided against pushing further into the issue, with time constraints in mind, as the final build of the game, while containing the above bug, has no other errors or issues. Due to the time spent on this, and minor attempted tweaks, I have also not pushed further with the terrain implementation, but in hindsight, believe that had I approached the issue sooner, I would have been able to implement it within a week or two.

Minor UI Tweak
To help understand the weapon system, I have now added the weapon types of the current unit, and any unit the mouse passes over.
The names appear in different colours:
  • Weapons coloured green will attack positively
  • Weapons coloured red will attack negatively
  • Weapons coloured black are the same type

Post-Mortem
In the academic year that I have spent working on this project, I feel I have come a long way from the start. I can definitely look back and say I was over-scoping based on my initial abilities and knowledge, but towards the end, it was more unjust fear holding me back. Taking that step into AI was terrifying for myself, believing I was going to feel far too out of my depth, but to the contrary, once I got stuck into it, it became an area I really enjoyed playing with, even if the AI I have implemented is still considered simple.

There were times during the project when I came to resent it. I looked at the progress I made at worse times, and judged my ability on that, rather than the periods where I managed to understand and grasp concepts much quicker than expected.

My stubbornness did not help at times. Early during the project, I attempted (Realising now, rather foolishly) to implement my own form of pathfinding, because I felt like a part of me was cheating, learning directly how to implement it, but I've come to realise that is how you need to start at times, and it's how you progress it yourself that really makes the change. I look back at the initial tutorials I watched, and while I see remnants of what I implemented then, I've also very much made those aspects I learnt into my own aspects.

Five Things I Think Went Well
  • Having only used array's in a rather ham-fisted manner before this project, what I have learnt now about array's and list's has helped me further myself so much more, and greatly improved my coding capabilities, as I also feel have been demonstrated not only in this project, but the group project we completed this year
  • My AI implementation, while having a few bumps in the road, has gone better than I could have expected. I was very nervous to tackle AI, but once I made that leap, I realised I'd been rather stupid for being nervous. As well as learning how to create basic AI using decision trees, this also helped me stop being so nervous about jumping into unknown aspects. I just wish I had learnt this sooner within the project.
  • My A* Pathfinding has also gone well. It was an area I had no real clue about, and was another area I was nervous to step into. Following a step-by-step guide, I quickly found myself getting to grasps with it, taking in the information at a steady pace. It led to the A* being implemented at a much more rapid rate than I'd expected, eventually.
  • I found myself starting to add personal tweaks to code I had recently learnt, such as the changes made to the initial tutorials, and adjustments to pathfinding. While I had minor help in areas, I do believe a lot of it has been self-driven.
  • I managed to find my own way, for the most part, with AI, after reading and understanding the basics of Decision Trees. The AI is my proudest point, having been created by myself from scratch. I have this silly sense of pride that pushes me to try and find my own path in coding, rather than relying on tutorials and guides, so being able to create this AI from nothing but a decision tree, it's meant an awful lot to me, and really helped me feel so much more confident about my coding capabilities.
Five Things I Think Went Not So Well
  • From the beginning, I believe my timeline was flawed. Before putting together my proposal, I should have dug further into some of the aspects of the code I would be undertaking, rather than researching the theory on constructing Strategy RPGs alone. This would have given me a much better idea of timescale, rather than simply applying a week or two for certain aspects.
  • Again, I had been nervous at multiple points of the project, when looking to move into new areas that appeared daunting. Had I worked up the gall to move into my AI and A* earlier, I believe I could have been much more on track with my initial timeline.
  • The self-created grid and pathfinding I attempt to implement. I spent the better part of a month or two working on this, when in the end I completely scrapped it, and went onto implementing A* Pathfinding. Had I done this sooner, again, I believe I would have been much more on track with the timeline
  • Terrain implementation. This was an area I ended up skipping over, following the wasted time pre-A* implementation. While I had initially planned for the terrain to be dealt with shortly after this, my attention turned to fully implementing the Rock-Paper-Scissor system, and that led onto the creation and implementation of AI.
  • Skill and levelling implementation. Another key area I hoped to tackle, this again got swamped over by the RPS and AI systems, due to poor time management.
Five Things I Will Take Away From This Experience
  • Throw myself into unknown depths. There is no point in me feeling fear towards new, unknown areas of code, simply because I am tackling them by myself. The time constraints put on by the fact this was a piece of university work that needed to be handed in did mildly hinder, and my timeline was always at the back of my mind, telling me to focus on other areas that I had not tackled. Moving forward, I intend to have no fear or nervousness towards new areas and aspects of code. The best way for me to learn them, is to simply get stuck in.
  • Don't let pride stop me from going to tutorials and guides. As much as I want to prove myself to be a self-efficient coder, that does not mean that every single line I write needs to be created myself. I must look at how others tackle problems I may come across, and adapt those methods to make them my own.
  • Seek out help when it's needed. I thank my lecturer Chris Janes a lot on this one. He really helped guide me when I felt lost, even when it was something simple, such as incorrect syntax. At many points, I again felt nervous, but this time seeking help, due to this being a self-driven project, I didn't want to come across as if I was still needing someone to hold my hand. But, the tit-bits of advice I recieved, the odd mistake pointed out to me, without this, I dread to think where this project would have ended. I have a terrible tendancy of getting myself wound up over issues I cannot see an answer to, but with this project, I did my best to overcome that, even when it meant asking for some external help for something I felt was so insignificantly silly I'd just make myself look like a fool asking for help.
  • Learn to judge timescales more efficiently. I have a terrible sense of time when it comes to long-term planning, and this project made that highly evident. Instead of making sure I understood the various components I would be using in my project, I skimmed over them, alloting what I believed at the time to be suitible gaps of time. In turn, I suffered for it, as evidenced by how far my project is compared to my original timeline. I believe the best way for me to do this in the future is:
  • Plan, plan, plan. While I did research into Strategy RPGs as a genre, as stated above, I did not do enough research into the various components that would go into creating one. Had I researched AI sooner, I believe I could have saved a lot of time, and the same with A*. I need to make sure I can grasp what I will need to tackle, so I can suitably set a reasonable, realistic time to it. I should have done much more in-depth research before my proposal, but, sadly, hindsight is 20/20. Now I intend to move onwards and upwards with this lesson learnt the hard way.
Overall, while the project has definitely had it's ups and downs, I truly believe that I have come out a much stronger, confident coder for it, who will take that much more time planning and researching, to ensure the outcome is that much more efficiently implemented, and solidly designed.

Tuesday 10 May 2016

Finalised AI Implementation, and further bug-fixing and tweaking

Continuing on from Continuing AI Implementation & Bug-fixing, I have now finished my AI to a higher standard than expected, and started tweaking and editing code within my project.

AI
My AI implementation is now finished, working as hoped for. I came across one small bug, the moving boolean on AI units was not resetting after their turn, meaning they would not carry out a movement the following turn. This has now been fixed by adding moving = false to my list of variables that are reset when the AI's turn has finished.

I have now implemented the statements required for if the AI is not in range to attack a User Unit, or be attacked by them. Originally, I designed it so the AI would stand their ground, and use a health vial if needed, but realised how bothersome this could be to a player, so have no included my AI to move towards the closest user unit, providing they do not end up in range of them. This means the AI will always attempt to move in on user units when possible, and retreating if they acknowledge a user unit can reach them next turn. I feel this makes the AI move and attack more naturally, rather than simply waiting for the player to send their units into range.

Inventory
I have now tweaked the inventory system, so if a user unit has any additional weapon items in their inventory, when the corresponding inventory button is pressed, it is set to the equipped weapon.


Unit Classes
I have now altered the unit classes, creating 3 specific classes for the AI. These classes are weakened versions of the user units classes, that still utilize the same weaponry. In play testing, this has proven to lower the challenge for the player, but give them a better chance to get a feel for the gameplay.


Unique Support Mechanic
I have now implemented a Unique Support Mechanic into the game. Simply, when a user unit attacks an enemy, if there is another user unit on the opposite side of the enemy, both user units attack. This only effects user units, so the enemy AI cannot utilize this tactic.



Unit Positioning
To simplify play-testing, I have now added a Vector2 Array to the GameManager class, that sets the positions of the units. While this array has to be manually changed to cater for additional units, it has allowed me to alter conditions in the project at a much faster rate than hard-coding their positions. Also, opposed to the block of positions I initially had, to allow quicker changing of unit positions, I have now implemented them into a for loop, meaning the only variable that needs to be altered for the positioning is the array size.


Final Days
As the deadline for the project approaches, I intend to continue tweaking what has already been implemented, ensuring it flows and works as best possible. I still would like to tweak the combat itself, and get a good balance worked out for the stats.

Thursday, I intend to write up my Post-Mortem on the project, as I feel I have learned a lot throughout this project, in coding, problem-solving, planning, and how best for me personally to approach certain issues and problems.

Monday 25 April 2016

Continuing AI Implementation & Bug-fixing

Following on from my last post, Further AI Implementation, I have continued on with the enemy AI, now implementing some of the desired Not in Range decisions from the decision tree in Reading: 'Artificial Intelligence for Games', and AI Planning. The Enemy AI will now: 

  • Judge if it is in range of a User Unit
  • Will check their health and inventory, to find out if they need healing, and have a health vial to do so
  • Will flee from User Units if they are in range of them, in the opposite direction so they are one tile out of range
Before reaching this point, I discovered and amended a few bugs:

Current Unit Index
I have been using an int, currentUnitIndex, to cycle through units in the units list. If the index hits the units.Count, it is reset to 0, starting the cycle again. I discovered that when a unit died, and it went to their turn, this was throwing off the index, throwing back errors. Thankfully, I managed to come up with a simple solution to this. In the gameManager Update method, I added a single if statement:


        if (currentUnitIndex >= units.Count)
        {
            currentUnitIndex = 0;
            pathfinding.target.transform.position = units[currentUnitIndex].currentTile._pos;
            pathfinding.FindPath();
        }

This means, if the Index attempts to go over the count, it will automatically be knocked down to 0, and the pathfinding target will be reset to the current units current tile position.

Movement Issues
Having thought I had handled all movement issues, I soon realised that I hadn't, when I could move the first user unit, but the second would not draw a path. I realised that the path target was not resetting, and the path itself was not being drawn. I handled this by adding a single line to my Move() method:

    public void Move()
    {
        if (units[currentUnitIndex].moving == false)
        {
            units[currentUnitIndex].moving = true;
            units[currentUnitIndex].attacking = false;
        }
        else
        {
            units[currentUnitIndex].moving = false;
            units[currentUnitIndex].attacking = false;
            pathfinding.target.transform.position = units[currentUnitIndex].currentTile._pos;
        }
    }

By adding in the bold line above, it meant that the path target would be reset, and would start to find the path correctly once the mouse starts to move over tiles.

AI Implementation
Having tackled the two above bugs on the previous Friday, Saturday I started with the actual implementation again, adding the functions listed at top. Once I had implemented these features, I decided to try out a couple potential actions the AI could perform, leading to this:
When Enemy A attacks, Enemy B attacks too
First I tried two units in range, to ensure that pathfinding and AI decision making were working for in range, still. After this, I made a horrific discovery:
If Enemy A retreats, Enemy B's movement failed
If Enemy A retreats, when Enemy B goes to attack, it never finishes its movement. At this point, I was aghast, having spent the previous day ensuring correct movement. Concerned that I may be breaking the code, I called it a day at this point, and went to my lecturer on Monday, to discuss the issue, and see if he could point out the source of it.

We discussed what I had done, and I demonstrated the issue. Upon debugging the moveCurrentPlayer co-routine, we discovered that, instead of following the path tile at a time, as intended, it was making large jumps within the path, moving One square, then Two at once, then Three, coming to a stop there. (In this case, two tiles away from the User Unit.)

I had set up the move method as an IEnumerator co-routine, to make sure of yield. This is what allowed units to "walk" from tile to tile. It had led to some issues previously, and I had realised at an earlier point in the project that this may cause issues further down the road, as I consider this method rather cheap and dirty.

When the IEnumerator was changed to a standard void method, with the yield removed, while units would teleport to the destination tile, using the above scenario again, Enemy B would successfully move and attack. My lecturer deduced that it may have been the yield causing the issues.

For now, I have changed it permanently to a void method. I intend to finish the rest of the AI, and attempt to implement the Unique Support Mechanic before the project is finished, with my AI being made a priority. Should time allow, I will then attempt to add the visual movement back into the project, using a cleaner method.

I realise now that I did take a risk using the IEnumerator; it was the quickest option at the time, and did what was required, but had I put extra time into creating my own movement system, or using one based around Time, it could have saved me time at this point in the project, but oppositely, I could have got stuck in working that out, which could have possibly delayed me further, but regardless, has made me consider risk/reward situations in coding to a greater extent in the future, and is something I will have to start implementing in my work ethics.

Below you can find all code relating to the project up to the 25th of April, 2016.

Saturday 16 April 2016

Further AI Implementation

Following on from my last post, AI Implementation, I have now discovered where I was going wrong with the path lengths, and furthered the AI decision making to encompass one side of my decision tree.

With the path lengths issue, I now realise that my base understanding of Unity was flawed, and this is where the issue stemmed from. While I was changing the target in the turn update, the path was not being retraced. I had believed that Update() methods ran parallel, meaning all update methods ran at the same time. My lecturer explained to me that instead, Unity ran through game objects, running the update method on them sequentially, based on Unity's built-in building system.

To deal with this issue, I was told that I would need to retrace the path after the target had moved. This was done by creating a public FindPath() method in pathfinding.cs. This is then called on every time after the target has been moved, returning the correct values for the path count.

Once this was working correctly, I could move onto the first point of my decision tree: 

Is the enemy in range?
As I could work out the path lengths, this meant I could now determine which User Units would be in range of the AI. For each User unit, the target positioned itself on them, retraced the path, then compared the path<> Count - 1 to the AI moveLimit. The minus 1 is added, as the AI does not need to be on the same square as the user unit, but an adjacent square. If the path count is up to and including the user units current node, it does not need to be counted. If the path count - 1 is less than or equal to to the moveLimit, that user unit is added to the inRange<> list.

Is there more than one? and Does my weapon beat any of theirs?
Originally I had intended to check the inRange list once it was populated, check to see if there is more than 1 in the list, and if so, check through that list to see if any user unit equippedWeapon weaponTypes matched the AI's equippedWeapon's posType. If true, they would have been added to the posWeapType<> list.

Instead of this, I ran an if statement after a unit had been added to inRange, checking the weapon type. If it returns true, that unit is also added to the posWeapType list. So, if there is only one unit in range, that will become the target, but if there are multiple, and one falls in the posWeapType list, they will default to the target.

While working on the AI, I started to notice some issues with the movement:

  • When a user unit moved, the path would remain constant, from the initial starting point, to the target, until the unit had reached the end of the path.
  • At the same time, when the AI moved, the path was retracing, shrinking with the movement of the AI, throwing off numbers, meaning the AI would often be one or two tiles behind where they should have been.
  • I changed a lot of the moveCurrentPlayer's if statements, in an attempt to streamline them, and ensure it worked with no errors for both a target set beyond the current units move limit and a target's which has a path shorter than the current units move limit.
  • The main issue was stemming from the fact I was setting the pathfinding.cs bool pathTraced to false, so the path was getting retraced when it shouldn't. I placed the FindPath() method at the end of the function, and this seemed to sort out all movement issues.


Currently, the AI defaults to attack the first in the list, depending on if the bool for a unit / units being in the posWeapType returns true, or just units inRange.

My next steps will be to implement the AI decisions for no enemies in range, and then tidy up the AI coding, streamlining it as much as possible.

Thursday 7 April 2016

AI Implementation

Following my last post; Reading: 'Artificial Intelligence for Games', and AI Planning, I have now started attempting the implementation of the pseudo-code created.

To start with, I have now given units a tile variable, allowing them to know what tile they are on specifically, at all time. I had done this initially for the IndexOf value of the node they were on, as specified in my pseudo-code from my previous post:


This is the code that has been implemented so far. The main issue I have been facing with this, is incorrect values for the path lengths:

I added a simple input, where pressing 'z' will give me Path Count.
As seen at the bottom, '22' was the value returned for the topmost User Unit.

For the User Unit to the right, '6' was the value returned for the path count.
Finally, '4' was the value returned for the left User Unit
The issue is, the results generated by the current AI code seem very far away from this (With the if statement disabled for clarity):
As can be seen, one loop has all path counts returning as '36', with every loop afterwards returning with a value of '6'. All Grid Positions shown are correct.
From what I am able to understand, once initial loop has been made, the target is left on the right-hand User Unit. While the names and grid positions are cycling through, there seems to be an issue with the target itself, though I cannot see any issue within the code, other than perhaps the fact that the change in positioning is not able to keep up with the loop it is in.

With this issue at hand, I will have a discussion with my lecturer, to clarify that my current code and pseudo-code is viable, and to find out if the issue I am facing is efficiency issues, or incorrect code.

Wednesday 6 April 2016

Reading: 'Artificial Intelligence for Games', and AI planning

As I have now implemented the inventory system into my game, following my previous post The Inventory System, I have put time into researching AI development for games. At this point, I have never tackled AI to a high level standard, with most consisting of AI repeating an action, or homing in on a player character. To learn more, and hopefully find a starting point, I read Artificial Intelligence for Games (2009, Millington, I & Funge, J). Below are the notes I have taken from my reading:


Artificial Intelligence for Games

Millington, I & Funge, J

1.2 Model of Game AI

The AI Model provided within the book.

Model splits AI tasks into three groups; Decision Making, Movement, and Strategy.
Decision Making & Movement contain algorithms working on a per-character basis.
Strategy operates on a whole team.

Board games (Chess, Risk) only require Strategy level.
Platform games (Jak and Daxter, Oddworld) only require Decision Making and Movement level.

1.2.1 Movement

If a character is attacking in melee, they will home in on the player, only activating the attack animation at a certain range.
Movement can be more complex; e.g. Splinter Cell, if the player is seen by a guard, they may attempt to locate the closest wall-mounted alarm, which can involve navigation over long distance.

1.2.2 Decision Making

“Involves a character working out what to do next.”
Will typically have a range of behaviours to choose from, decision making system need to work out the most appropriate behaviour at the given time.
Zelda games; farm animals stand still, move a small distance if bumped into.
Half-Life 2; enemy AI attempt multiple strategies to reach the player.

1.2.3 Strategy

Most action-based 3D games use only Decision Making and Movement.
“Strategy refers to an overall approach used by a group of characters.” In context of the book.
Will often still require characters to have their own Decision Making and Movement, with Decision Making being influenced by the group Strategy.

1.2.4 Infrastructure

To build an AI, a whole set of additional infrastructure is required.
Movement requests must be turned into action in the game.
AI requires game information to make sensible decisions, sometimes called “perception”; “working out what information the character knows”.
“World Interfacing is often a large proportion of the work done by an AI programmer”, and often the majority of AI debugging.
AI systems need to be managed to use the correct processor time and memory.

1.2.5 Agent-Based AI

Focused on “producing autonomous characters that take in information from the game data, determine what actions to take based on the information, and carry out those actions.”
Can be seen as a bottom-up design:
  • ·         Work out how each character will behave, and the AI required to support it
  • ·         Overall behaviour of the game is a function of how character behaviours work together
  • ·         Decision Making and Movement make up the AI for an in-game agent

Non-Agent-Based AI act oppositely, from top to bottom. E.g. Pedestrians and traffic in Grand Theft Auto 3. Traffic and pedestrian flow is calculated via the time of day and the region of the city, and is only turned into singular cars and pedestrians when in view of the player.
“A good AI developer will mix and match any reliable techniques that get the job done, regardless of approach”.

1.3 Algorithms, Data Structures, and Representations

1.3.1 Algorithms

They are step-by-step processes, generating a solution to problems faced by AI.
Data structures hold data that allow the algorithm to quickly manipulate it and reach a result.
Data structures must often be tuned for one specific algorithm.
Set of elements must be understood to implement and refine an algorithm:
·         The problem that the algorithm tries to solve
·         A general description of how the solution works, including diagrams where needed
·         Pseudo-code presentation of the algorithm
·         An indication of the data structures required to support the algorithm, including pseudo-code where required
·         Particular implementation nodes
·         Analysis of algorithms performance: Execution speed, memory footprint, scalability
·         Weaknesses in approach

5.2 Decision Trees

“Fast, easily implemented, and simple to understand.”
Simplest technique discussed in book, with the potential to become quite sophisticated.
Very modular and easy to create.

5.2.1 The Problem

Using a set of knowledge, corresponding actions must be generated from a set of possible actions.
Mapping between input and output can become complex.
Same action may be used for multiple inputs, but a change in one input value can turn that action from sensible to stupid.
Require a method capable of easily grouping lots of input together under one action, while allowing the significant input values to control the output.

5.2.2 The Algorithm

Decision tree is made up of connecting points, starting at a “root” decision, with ongoing options being chosen thereafter.
Example of a decision tree

Choices are made based on the characters knowledge, often referring directly to the global game state, rather than a personal representation.
Algorithm continues along the tree until there are no more decisions to consider. An action is attached to each leaf. Once a leaf is reached, the relating action will be carried out.
Tree decisions will often be simple, with only two responses.
NOTE: Even just at this point, I feel a lot more confident pushing into the AI, structuring a tree in my mind that will be put onto paper. Though I have seen tree diagrams and flowcharts used in many ways previously, it never occurred to me that it could be implemented into AI Decision Making.
Common for object-oriented engines to allow the tree to access methods of instances.
To AND two decisions, they are placed in series in the tree; “I A AND B, then carry out Action 1, otherwise carry out action 2.”
To OR two decisions, they are placed in the opposite series in the tree; “If A OR B, then carry out Action 1, otherwise carry out action 2.”
An AND and OR tree
As decisions are built into the tree, the amount considered will be much smaller than the amount within the tree.
Can be built in stages; simple tree can be initially implemented, with additional decisions added on as needed.
Can be given more than two options to choose from;
Guard in a military facility, decisions based on current alert status; green, yellow, red, or black. Using binary decisions, the alert value must be checked three times. Using multiple branching, the value can be checked once for the decision.

Implementation of multiple branches are not as easily optimised as binary. Majority will always be binary.


Following on from this reading, I felt like I had a much clearer understanding of how to tackle my AI, and went about creating a simple decision tree for my AI:
Using this tree as a basis, I have gone on to pseudo-code the process:


While I believe there may be further tweaking required once implementing the code, the process of creating the decision tree, and writing some basic pseudo-code for the process, has greatly helped my learning process, and eased many concerns I had towards AI.

Tomorrow I shall begin implementation of the code, and hopefully have a level of working AI before the week is out.

Bibliography

Millington, I & Funge, J (2009). Artificial Intelligence for Games. 2nd ed. Burlington, MA: Morgan Kaufmann. p8-12, 295-300.

Monday 21 March 2016

The Inventory System

Continuing on from my last post, Final A* Implementation and Inventory, I have now made much further progress with the inventory system, with it now being implemented to a functional level.


  • The Unit class now has a functioning inventory list, with Item classes now being pushed into it.
  • I have attempted to add a small level of security to the Item class, by storing its variables as private, and having them accessed externally via get methods.
  • The first entry in the inventory is registered as the active weapon, with combat now being slightly adjusted based on this. Further fine tuning will be required for the combat mechanics
    • This needs to be slightly tweaked, so that certain unit classes can only use a specific type of weapon.
  • A health vial has been implemented. When clicked on in the inventory, it will restore a units health, provided they are injured to begin with.
    • This was one of my key concerns with my project at this point. The current units inventory is represented by 5 buttons on the UI. As the inventory could end up having items added and removed from it, I wanted to make sure that the button corresponding to an item, based on position in the inventory, would always run the desired function.
    • Prior to this, I had only used UI buttons OnClick() inspector mechanics, manually adding the desired function to a button. I had to do research into dynamic button assignment, which lead me to this post on the Unity3D forums.
    • Initially, I had tried the following:

for(int i = 0; i < units[currentUnitIndex].inventory.Count; i++) {
            if(units[currentUnitIndex].inventory[i].itemType == Item.ItemType.Tool) {
                    inv[i].onClick = units[currentUnitIndex].inventory[i]._heal(units[currentUnitIndex]));
            }

        }

    • This lead to no success in the slightest. Upon finding the forum post mentioned above, I tweaked the for loop slightly, leading to this:

for(int i = 0; i < units[currentUnitIndex].inventory.Count; i++) {
            if(units[currentUnitIndex].inventory[i].itemType == Item.ItemType.Tool) {
                    inv[i].onClick.RemoveAllListeners();
                    inv[i].onClick.AddListener(delegate { units[currentUnitIndex].inventory[i]._heal(units[currentUnitIndex]); });
                }
            }
        }
    • While this for loop lead to some results, I kept having errors return, stating that the index was out of range. Starting to panic, I decided to create an Item temporary variable within the loop, storing the Item used in there, and calling on the _heal method from there. At this point I also added an if statement, declaring that the method would only be available if the Item name was "Health Vial", but realised shortly after that it was not required, due to how I set the _heal function up. My for loop ended up as so:
        for(int i = 0; i < units[currentUnitIndex].inventory.Count; i++) {
            if(units[currentUnitIndex].inventory[i].itemType == Item.ItemType.Tool) {
                    Item item = units[currentUnitIndex].inventory[i];
                    inv[i].onClick.RemoveAllListeners();
                    inv[i].onClick.AddListener(delegate { item._heal(units[currentUnitIndex]); });
            }
        }

    • By storing it as a temporary variable, this seemed to remove any issues with the index being out of range, allowing it to work with no visible issue, and no errors.
    • Having contemplated the removed if statement during the writing of this, I have decided to add it back, as I may add items that effect stats temporarily, meaning that Item.ItemType.Tool will not only be used for healing units:
        for(int i = 0; i < units[currentUnitIndex].inventory.Count; i++) {
            if (units[currentUnitIndex].inventory[i].itemType == Item.ItemType.Tool) {
                if (units[currentUnitIndex].inventory[i].Name == "Health Vial") { 
                    Item item = units[currentUnitIndex].inventory[i];
                    inv[i].onClick.RemoveAllListeners();
                    inv[i].onClick.AddListener(delegate { item._heal(units[currentUnitIndex]); });
                }
            }
        }
  • This now means that combat is being dictated much more by weapon choice and type, and a health Item can be applied to units.
  • As a small bug fix, I now have changed code around, so that the path is visible to players, and only shows the path up to the amount of spaces a Unit can move. While I had attempted to implement this earlier, I realised only recently that an if statement within the Tile update was turning them white, meaning the path could never be fully visualised.
My next steps will possibly be my most challenging, as I am aiming for AI implementation, as it has been put off due to other aspects of the project. This will start off with readings during this current week, and hopefully start implementation by next week.

There is one more aspect I would like to attempt to tackle before this too. At this current point, units are being instantiated in based on the worldSize for positioning. Instead, I would like to be able to use the grid[ , ] array to position the units, as this will make for more direct instantiation of units, requiring much less math to place units initially. While this will not be my primary focus, I believe it will aid in AI implementation, in terms of positioning AI units.




Sunday 13 March 2016

Final A* Implementation and Inventory

Following on from my last, Continuing with A*, I met with my lecturer Chris Janes, I have now got my A* pathfinding working much more accurately.

Last time, I was struggling to alter the diagonal movement within the pathfinding. With the advice I was given at my meeting, I have now altered the pathfinding to work along horizontal and vertical connections.

Previously, the pathfinding would look at neighbouring nodes (public List<Node> GetNeighbours in Grid.cs) by creating a 3x3 grid, with the current node in the centre, and checking every position in the grid bar the centre to determine how to move. Now, when neighbours are being added, it simply calls on the nodes positioned above, below, left, and right. I had originally attempted to alter the use of the 3x3 grid, cutting out diagonally placed nodes, but it was pointed out that I could achieve this much quicker by simply calling on the 4 neighbours the node required.

Movement has been tweaked, so there is a slight delay in a unit moving from space to space, giving a sense of visible movement. While I have issues that will be ironed out later, at this point, I now have a successfully working pathfinding system.

Following this, I decided to start looking towards my inventory system. Going along the ideas explained in my previous post, I still believe that it will be most beneficial to get an inventory system implemented, before moving onto AI.

I have created 2 new classes, the Item class, and the Items class:

  • The Item class is a representation of an item itself. It can function as either a Weapon or utility item, currently using the variable stats Damage and Hit Chance, or Heal amount.
  • These items are created within the Items class. Though currently containing only one, the items class will have a method for every item available within the game. The method creates a new Item class, assigning the required stats, then pushes it into the players Inventory List.
At first, I ran across some issues:
  • When I initially tried the method above, I received a warning stating that a MonoBehaviour based script, I could not create it as I was attempting to:


public void IronSword(Unit unit)  {


            Item item = new Item(0, "Iron Sword", 5, 10, 0);

              unit.inventory.Add(item); 
      (This was originally being pushed externally. In the generateUnits method in GameManager, the item was being called to be created through the Items script, being stored as a public Item variable on that script, then trying to move that Item into the Unit's inventory.)
            }
        • Following this, I removed the MonoBehaviour from the Item class. While I was no longer receiving the warning, I was getting Errors stating that, once the item was being pushed into the inventory, that no object existed to be pushed.

        • Not clear on why this error was being received, I instead chose the opposite route; to work with the item as a MonoBehaviour class. To do this, I had to use AddComponent(), to attach the item class as a script to the unit gameObject. Due to how the Item class has been set up (Visible below under Item.cs), I could create a new Item class, but I could not input the values required, instead leaving a blank item class on the unit.

        • As this was getting me nowhere, I went back once again, removing MonoBehaviour again from the Item class. I attempted the method seen above, with unit.inventory.Add(item) being added at this point. The unit in question is currently being set through the generateUnits method in GameManager. As items outside of set up should only be accessible to the current unit at the time, I will be able to call on the current unit as the (Unit unit).

        Overall, while I still have some steps to make to finishing the inventory system, I have found it much easier to implement than expected. My main concern at this point is the sorting and re-ordering of Inventory List. While I may be able to create a work-around, I intend to keep the active weapon in space [0] of the list, rather than requiring further bools in the Item class, or for the player to specifically keep referencing items within the inventory.

        This shall be completed and implemented around the middle of next week, so AI implementation can be ideally before next week is out.

          Saturday 27 February 2016

          Continuing with A*

          Continuing on from my last post, Further Implementing A*, I have continued to refine its implementation into the game.

          I have now got units following the path given to them, with the path being fixed upon starting movement. The unit will now appear at the end of the path, moving to each position, currently instantaneously, so there is no visual representation of movement displayed. 


          Units now have a movement limit. While a path can be drawn across the grid, the unit will only move as many spaces as its move limit allows it to.


          I am still having issues with diagonal movement. I believe that I will need to adjust the neighbours being found in the grid class, and the values being assigned for movement in the pathfinding class. While I have looked online for various solutions, e.g. Stack Overflows answers (A star algorithm without diagonal movement) but so far have been unable to find anything that I can grasp and adapt to my current code.


          Now that I have the units moving along their given path, I feel I have a couple of choices on how to progress:



          • Continue with the pathfinding
            • Pros
              • Attempt to remove diagonal movement
              • Create visualisation of unit paths and movement
            • Cons
              • Been previously delayed by pathfinding issues, do not want to repeat
              • Could cause further delays in timeline
          • Implement AI
            • Pros
              • Get back on track with timeline
              • Potentially make progress on a personally worrying aspect of the project
              • A fresh aspect of the project to break me away from pathfinding
            • Cons
              • Would need to refine later down the road once weaponry have been implemented
          • Implement weaponry and inventory
            • Pros
              • A fresh aspect of the project to break me away from pathfinding
              • Ready for AI implementation
            • Cons
              • Takes focus away from other key areas of the project
          I believe, when broken down, my best bet would be to look towards weaponry implementation now. I add " A fresh aspect of the project to break me away from pathfinding" as a pro, as I believe that if I focus too much time and effort into this singular aspect, a mountain will be made out of a molehill, and I will be less and less inclined to work on the project out of fear/panic. 

          By shifting away from pathfinding, even if it's for just one week, I will be able to approach it again from a fresh angle, that will aid me in finalising it. While there are still more Pros leaning towards AI implementation, I feel the extra work that would be required later down the road, to alter the AI to work with weaponry, is completely unneeded extra work. 

          Should I implement weaponry now, shifting the Rock-Paper-Scissor system over onto that, I will be able to focus much more heavily on the AI, without having to overhaul it later on. While it does throw off the order of my timeline, I should be able to end up in the same position as I would be following my current order, whereas I worry that AI implementation or continuing pathfinding will delay me further.


          Week
          Timeline
          New Timeline Milestones
          15
          Refine the RPS system, and introduce weapons that break away from the base system
          Implement what I have learnt about A* into the current build of the game, so player units move using the A* pathfinding
          16
          Refine AI within the game, so they can intelligently attack units based on position and weapon
          Ensure that the A* pathfinding is correctly implemented, and look to implementing terrain modifiers within the map
          17
          Continue to refine AI
          Research further and implement AI into the game, ideally with refined goal-seeking mechanics
          18
          Implement an inventory system, allowing units to equip different weapons, and the use of a health potion item
          Ensure the AI is functioning to an optimal level, able to seek out the most efficient target
          19
          Look into a levelling system, and how this can be implemented
          Implement a weapons system to take on the RPS system
          20
          Implement the levelling system, and start refining stats mechanics
          Ensure the weapons system functions correctly, and research into levelling mechanics
          Easter Break
          Refine the unique support concept, creating a document outlining the mechanic, and its uses
          Refine the unique support concept, creating a document outlining the mechanic, and its uses
          Easter Break
          Continue to refine the support mechanic, and start implementation if possible
          Continue to refine the support mechanic, and start implementation if possible
          21
          Ensure the AI, movement, levelling, and combat mechanics are operating as desired
          Finalise support mechanic implementation, and refine stats for combat mechanics
          22
          Start looking into a mobile conversion, and implement if possible
          Ensure that any map modifiers are functioning correctly, and AI is functioning efficiently
          23
          Start or continue to implement mobile conversion
          Look to start moible conversion
          24
          Finalise mobile conversion
          Finalise mobile conversion