Jump to content
  • Advertisement


  • Content Count

  • Joined

  • Last visited

Community Reputation

1102 Excellent

About Gamieon

  • Rank

Personal Information


  • Twitter
  • Github
  • Twitch

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

  1. https://gamieon.itch.io/field-of-heroes [media][/media] After just over three months of working on it in my free time, I just released an online multiplayer soccer MOBA on itch.io as a prototype. This is a departure from my normal routine where I come up with an idea, develop it through to the end, and release a polished version of it. Backstory and Motivation I got the idea for Field of Heroes back in 2014 when I was tinkering with the Unreal Editor. It seemed like a good project for learning how UE and behavior trees work, and I was inspired by mechanics from games like Altitude and Pigskin 621 A.D.. Back then I worked on it for a few months before moving it to the backburner since there wasn't a real interest in it from anyone else. I revived the project in Unity this past October after another developer released "What the Box?" on Steam after just one month of work, and it went viral. I was so inspired by their success that I just had to give it a shot. I don't want to develop fully featured polished games alone anymore because I don't have the kind of free time and energy that I used to. I therefore decided I would make and release Field of Heroes as a prototype to see if it generates a lot of interest. If it does, then I'll put more effort into it and maybe work with an established studio to make it really polished and much more content-rich and fun. If it doesn't then I'll keep it available for free indefinitely and move on to a smaller project. Have any of you out there reading this been in a similar position? What did you do and how did things work out?
  2. Foreword: I'm developing an online soccer MOBA where upwards of ten players will be jockeying for a soccer ball. This journal is a brain dump to help me get a handle on the mechanics of managing a soccer ball in an online soccer game using Unity3D and the Photon network engine. I apologize in advance for the lack of helpful diagrams, animated GIF's, and the covering of every single edge case from start to finish; I'm saving those for the day when I can confidently write an article on "These are the mechanics of ball management in a network soccer game which work well enough for a polished release." Game Environment The game is made with Unity using the Photon network library. All players can send messages to each other, and one player is the Photon "master client" which I'll refer to as the "master player" going forward. The master player is the authority that resolves any disputes among the peers; for instance whether a goal was really scored, and how much damage a player really takes from another's attack. Without a master player, the minor differences in everyone's game experience due to latency would escalate to become major differences, and everyone could be completely out of sync before long. The Ball There is only one ball on the soccer field. When no player possesses it, it rolls around the field managed by the physics simulation. After a player takes possession of it by running onto it, the ball will always be positioned in front of them (it becomes synchronized to the player's transform instead of its own transform). I'm purposefully leaving the details of who controls the physics simulation and who decides what player possesses the ball out here; I explain those things in the various implementations below. Ball Control Implementations Failure: Ball physics centralized, player control centralized My first implementation had the master player doing all the work: They would manage all the ball physics, detect when players were near enough the ball to possess it, and other players would have to send an RPC to the master player when they wanted to kick the ball away. This implementation worked poorly because all the non-master players had to wait on the master player to give them ball possession or relinquish it. If I were a non-master player, I would not appear to possess the ball by running onto it; I would instead have run over it and even a few steps in front of it before the ball attached itself to me. When I kick the ball, it would stick to me for a brief moment before being teleported back to the point I actually kicked it from. Failure: Ball physics centralized, player control decentralized My second implementation had the master player still managing ball physics, but each player would determine when they possessed the ball and when they kicked the ball. They would tell the master player when they did either. The master player would do a check to determine whether the request was legitimate, and then relay the ball's final owner to all the players. The problem here is that when the ball was kicked by a player, the ball would rubberband backwards on that player's instance. This is because the master player resumed the physics simulation from the kick point at the time the ball was already far away from the kick point on the kicker's instance. Potential success: Ball physics decentralized, player control decentralized My third implementation follows these rules: The PhotonView owner of the soccer ball is always the master player When a player possesses the ball, they control everything about it and stream ball position and rotation information to other players (the streaming is done through OnPhotonSerializeView calls in a hidden GameObject belonging to that player) If the master player disagrees with the player possession, it will override it and notify all players through the ball's OnPhotonSerializeView call Here is the step-by-step process of what happens when a non-master player takes possession of the ball and kicks it later: Ball is rolling across the field. Player A controls the ball and is simulating its physics. All other players are getting a stream of ball position and rotation updates from Player A. Player B moves their player to the ball to possess it. Player B sends an RPC to all other players that it "tentatively" possesses the ball; its own session locks the ball to its character position and makes its rigidbody kinematic. All other non-master players get the RPC and lock the ball to Player B's position also making it kinematic. The master player gets the RPC and determines who should be in possession of the ball; in this case it is indeed Player B. The master player updates the ball's serializable data to reflect that, and its own session locks the ball to Player B's position and makes it kinematic. All the non-master players (including Player B) call OnPhotonSerializeView on the soccer ball and see that the master player confirmed Player B owns the ball. No action is needed. Player B decides it's time to kick the ball. Player B makes the ball not kinematic any longer, adds a kick force to it and sends an RPC to all other players that it kicked the ball. Now that the ball is in motion, Player B begins streaming the ball's position to all the players from its hidden GameObject. All other non-master players get the RPC and unlock the ball from Player B. Since Player B is streaming its position and rotation, the ball immediately begins to move in the direction of the kick. The master player gets the RPC, unlocks the ball from Player B, and updates its serializable data to reflect that the ball has no possessor. Since Player B is streaming its position and rotation, the ball immediately begins to move in the direction of the kick. So what happens if two players claim the ball one right after the other? All "tentative possession" RPC's after the first one are ignored until after the ball's serializable data update is received from the master player. This is accomplished by tagging each possession assignment with a sequence number that is incremented only by the master player in the serializable data payload. So what happens if the master player decides Player B doesn't possess the ball in step 5 because it's too far away for example? The master player will populate its serializable data with the fact that Player A still controls the ball. All other players, including Player B, will receive that and update the possessor on their instance accordingly. Player A will resume ball physics simulation. The kludge There's a bit of a kludge with the physics however: By the time the master player denied Player B's possession, Player A will have already stopped simulating the physics. Consequently when it resumes ball physics simulation, the ball will be at a dead stop in front of Player B. The only time this is avoided is if Player A is also the master player. There are some ways to resolve that kludge: Make it so Player B is never denied ball possession (and therefore cheating becomes easier) Make the most recent possessor, rather than the master player, verified the ball's transfer of ownership. I tried that and failed because I couldn't figure out the logistics of how to get all the players to agree on who was the most recent possessor. Outcome When I had two network players play with the ball one at a time using this implementation, the ball movement appeared natural in both sessions during all 11 steps. How well will this work with 3+ players actually playing the game? I don't know, but I'm fairly sure it will work better than my first two failed implementations.
  3. Recently I downloaded MagicaVoxel which lets you create and modify blocky objects, and bought PicaVoxel which lets you import them into Unity3D projects. The purpose was twofold: to help my friend Highsight develop his first game, and to have access to powerful tools in case I ever wanted to make a voxel art game. Highsight claimed difficulties with Rigidbody objects flying through the imported voxel objects. Upon looking at how the MeshColliders were built for the voxel objects, I guessed that the issue was with the fact the colliders were paper thin. Seeing that all of the voxel faces were one-sided, I figured that the best way to handle the issue was to create a BoxCollider behind every face. The script below, when attached to a voxel object, will iterate through all Chunk objects and generate bounding boxes for them on simulation startup: (Download the script from http://gamieon.com/blogimages/devblog/20160918/PicaBoxColliderGenerator.cs or copy from below) using UnityEngine;using System.Collections;using PicaVoxel;/// /// Attach this script to a voxel object to generate box colliders for all the chunks therein/// when the game or simulation begins. The advantage of this over using existing mesh colliders/// is that these colliders have depth and are therefore less prone to objects going through or/// other weird collision behavior happening/// public class PicaBoxColliderGenerator : MonoBehaviour { /// /// The thickness of a box collider. Would be nice to auto-calculate this someday /// public float thickness = 0.1f; /// /// True if the box colliders should be static /// public bool staticColliders = false; // Use this for initialization void Start () { GenerateBoxColliders(); } /// /// Generates box colliders for all child chunks of this object /// void GenerateBoxColliders() { foreach (Chunk c in gameObject.GetComponentsInChildren()) { GenerateBoxColliders(c); } } /// /// Generates box colliders for a single child chunk of this object /// /// The chunk void GenerateBoxColliders(Chunk chunk) { // MAJOR ASSUMPTION: The object is not static (or else mesh optimization occurs and you'll get an error getting the mesh) // First get the mesh information MeshFilter meshFilter = chunk.gameObject.GetComponent(); Mesh mesh = meshFilter.sharedMesh; // MAJOR ASSUMPTION: Each pair of triangles is a rectangle // Now do for all rectangles for (int i = 0; i < mesh.triangles.Length; i += 6) { // Get the vertices of one of the triangles that make this rectangle // // v2 ------- v1 // | | // | | // v3 ------- * // Vector3 v1 = mesh.vertices[mesh.triangles]; Vector3 v2 = mesh.vertices[mesh.triangles[i + 1]]; Vector3 v3 = mesh.vertices[mesh.triangles[i + 2]]; // Get the two sides of the triangle which also make up two edges of the rectangle // connected by the same vertex (v2) Vector3 a = v1 - v2; Vector3 b = v3 - v2; // Get the normalized vector of the rectangle facing inward Vector3 n = Vector3.Cross(a.normalized, b.normalized); // Get the center of the rectangle by calculating the midpoint of two diagonally opposing points Vector3 c = (v1 + v3) * 0.5f; // Create an empty object at the center of the rectangle and make it a child of the chunk GameObject go = new GameObject("ChunkBoxCollider"); go.transform.SetParent(chunk.transform); // Position the object such that when we eventually add the collider, the "outermost" face of the collider // will be in the same plane as the rectangle go.transform.localPosition = c + n * thickness * 0.5f; // The object should always be facing the rectangle. This is so when we size the box collider, // we can be sure of the role that each axe has. Keep in mind that LookAt deals in world coordinates // so we need to adjust for the chunk's world rotation go.transform.LookAt(go.transform.position + chunk.transform.rotation * n, chunk.transform.rotation * b.normalized); // Now create the box collider BoxCollider boxCollider = go.AddComponent(); // Size the box collider boxCollider.size = new Vector3(a.magnitude, b.magnitude, thickness); // Make the collider static if desired go.isStatic = staticColliders; } }} The script has two properties thickness: How thick the colliders should be (0.4 works well) staticColliders: If true, all the colliders will be static Keep in mind that if your voxel object is static, the script will have problems accessing the mesh data. You can address this by making the object not static. Because of the time it takes to generate the BoxColliders, you may want to consider writing an Editor script based on PicaBoxColliderGenerator to generate those boxes at design time instead of run time. Here is how the scene looked with no colliders, and how it looked with box colliders. On testing it with box colliders, I had no issues with Rigidbodies flying through the voxel at reasonable speeds. Highsight found the same to be true as well. I hope you find this helpful! Even if you don't, please check out my portfolio and follow me! Games: http://www.gamieon.com/games Twitch: https://www.twitch.tv/gamieon Facebook: https://www.facebook.com/gamieon Twitter: https://www.twitter.com/gamieon YouTube: https://www.youtube.com/gamieon
  4. Though I've dabbled in game development since the late 1980's, it wasn't until October 26, 2004 that I incorporated Gamieon for fun, adventure, and maybe even a little profit. It's a part-time studio owned by myself and an investor who helps me cover expenses. Together we're the only employees of Gamieon. I do all the programming, releases, website maintenance, social networking and business upkeep. Tasks that are contracted out on a need-to basis include graphic, sound effect, music development and sometimes marketing. My full time job is medical software development; I've been employed by the same company for over seventeen years. I opted not to go into full-time gamedev largely because I don't want to leave my current job and because I like to treat gamedev as a hobby more than as a career (though it wasn't always that way). [color=#800000]Pre-Gamieon[/color] I initially learned to program in BASIC with help from my father. Years later I learned how to program in C; mostly self taught but with some help from a friend on a local BBS I called frequently. I conceived of many projects but never finished any until around 1994 when I programmed and released "Flash Infiltrator;" a free ANSI war game you could play online a "Major BBS" real-time with up to five other players. Within a year following I released "Flash Tankfire" which was a free Scorched Earth clone also playable online a Major BBS. The development cycle was "code-and-fix" for all that time, and I didn't really think about collaborating with other developers. I was having fun on my own, and had not come across anyone else who wrote games. After those two games I turned my attention toward learning DirectX and engine development. I wrote a basic 3D video engine and a fast smooth voxel terrain renderer like that of the "Mars" voxel demo shown below: (Source: http://rainmak3r.altervista.org/voxel/download/ukindex.htm ) Sadly I lost the code to those projects many years ago, so I have nothing to show for it here. Later in college I developed and released "Cycles3D." The game is a free clone of the original Tron light cycle simulation. The initial release got thousands of downloads within the first day of being available; and later I discovered it was downloaded so many times that it created a distinct spike in campus network traffic for a short while. That was the legacy I left the University of Dayton with . You can still download it at http://www.cycles3d.com College was also when I got a co-op job developing medical software. After graduation my boss set up a full-time game development studio called Astound for me because, back then, I wanted a full time job developing games and he thought it could be lucrative. It was a dream come true, and I jumped right in. At Astound I worked with another programmer on a 3D shooter called "Zeos Fighters." After a year of work I deemed the project a failure and returned to medical software development. Its failure can be summed up in one sentence: I treated Astound like a programming exercise, not a game studio. The itch to develop games never went away, so I started development on a 3D physics puzzle game in my free time called "Dominoze." It was during development of that game that I founded Gamieon in order to formalize its development and collaborate with other developers under a corporate entity. [color=rgb(128,0,0)]Gamieon's Unreleased Projects[/color] (Number in parenthesis denotes most recent year of development) [color=#008080]Dominoze (2010)[/color] http://www.indiedb.com/games/dominoze [media][/media] Dominoze is a 3D physics puzzle game, and though unfinished, the crown jewel of my personal game development experience. In each level you figure out how to arrange objects in a scene such that all the dominoes can be knocked over in one continuous chain. I think the idea is amazing but the execution was poor. I spent nine years building the game, editor and engine (even writing a lightmapper from scratch) only to walk away from what became an endless cycle of feature creep and no direction. You can read all about Dominoze at http://www.gamasutra.com/blogs/ChristopherHaag/20130312/188293/Dominoze_PC_Postmortem_Nine_Years_One_Programmer.php [color=#008080]Gamieon Construction Kit (2010)[/color] http://www.indiedb.com/games/gamieon-construction-kit [media][/media] This was a learning project for Unity network development and also an attempt to make a Rube Goldberg sandbox simulation consisting of physics and simple shapes. I like the underlying idea of a simulator where people can build and share contraptions online, but I never came up with a complete design. I abandoned it after a few months to move on to more exciting projects. [color=#008080]Cycles3D 2.0 (2012)[/color] http://www.indiedb.com/games/cycles3d [media][/media] I ported my old Cycles3D project to Unity on a whim to see how fast I could do it and to make it look pretty along the way. I stopped development because I lost interest, but it remains on GitHub at https://github.com/Gamieon/Cycles3D should anyone want to tinker with it. [color=#008080]Gauntlet Clone (2014)[/color] (This project has no homepage) [media][/media] This was my first immersion into Unreal 4 development. I wanted to make Unreal's cave fly-through demo into a small Gauntlet level. I did just that in a very short time; and it was fun to play even though players didn't take damage. I abandoned the project right after I made the video because I wanted to work on something else more original. All in all it was a good learning experience. [color=#008080]Field of Heroes (2015)[/color] http://www.indiedb.com/games/field-of-heroes [media][/media] This is my second Unreal 4 project, and one I'd like to see come to fruition someday with help from a big studio. FoH is a soccer game where players fight each other, literally, for control of the ball. Even if the project doesn't go anywhere, at least I got to learn how behavior trees and character animations generally work in modern game development tools. This project is in development at the time of this writing. [color=#800000]Gamieon's Released Projects[/color] (Number in parenthesis denotes release year) [color=#008080]Tiltz (2010)[/color] http://www.indiedb.com/games/tiltz [media][/media] Tiltz is a simple mobile game where you tilt your device and pull incline ramps down with your finger to guide marbles into a barrel. It was my first venture into mobile development and my first released game that used the Unity engine. You can read more about it in the post-mortem (which I called "sunset" at the time) at: http://www.indiedb.com/members/gamieon/blogs/game-sunset-report-tiltz At the time of this writing it's still available on these platforms: iTunes - http://itunes.apple.com/us/app/tiltz-deluxe/id460936061?ls=1&mt=8 Google Play - https://market.android.com/details?id=com.gamieon.tiltzdeluxe [color=#008080]Hyperspace Pinball (2011)[/color] http://www.indiedb.com/games/hyperspace-pinball [media][/media] The objective in this game, which is my second mobile and first desktop Unity game released, is to destroy aliens on the screen using pinball-like controls. It was my first attempt at a seriously successful mobile game, and my most successful game by download. You can read the post-mortem at: https://www.gamedev.net/blog/1606/entry-2255775-hyperspace-pinball-development-the-almost-sunset-story/ At the time of this writing, you can still get it from these links: iTunes - http://itunes.apple.com/us/app/hyperspace-pinball/id449062825?ls=1&mt=8 Google Play - https://play.google.com/store/apps/details?id=com.gamieon.hyperspacepinball Desura - http://www.desura.com/games/hyperspace-pinball BrassMonkey - http://playbrassmonkey.com/hyperspacepinball [color=#008080]Hamster Chase (2013)[/color] http://www.indiedb.com/games/hamster-chase [media][/media] Hamster Chase is a mobile accelerometer puzzle game where you tilt the device to get all the hamsters within their hamster balls onto their seed piles. It's my second attempt at a successful mobile game, and my first where I leverage a cartoony theme as many popular mobile games do. I commissioned Meta3D studios to do all the art, and NovyPR to do the release blast rather than trying to do both myself. It did not reach the success I had hoped for, and I never wrote a post-mortem. I think the game would have done better had I released it as a free app designed around a solid in-app purchase model, and made regular updates to it with new features and achievements. You can see the virtual hamster cage at http://www.gamieon.com/hammys/ and click on the hamsters. If you do it enough they may tell a joke! You can get Hamster Chase from: iTunes - https://itunes.apple.com/us/app/hamster-chase/id564548708?ls=1&mt=8 Google Play - https://play.google.com/store/apps/details?id=com.gamieon.hamsterchase Windows Phone - http://www.windowsphone.com/en-us/store/app/hamster-chase/0a538986-ef86-49ad-bb39-9b1bb8e9de24 [color=#008080]Domino Arena (2013)[/color] http://www.indiedb.com/games/domino-arena [media][/media] Have you ever had an idea for a game that floated around your head for over a year, and you knew it wouldn't be popular but you finally wrote it anyway just to get it out of your head? That's Domino Arena. The objective is to alter the course of falling dominoes that change color as they fall so that as many of them become your color as possible. I released it just to see how it would do, and as expected it didn't garner a lot of interest. You can play it from: iTunes - https://itunes.apple.com/us/app/domino-arena/id743797890?ls=1&mt=8 Google Play - https://play.google.com/store/apps/details?id=com.gamieon.dominoarena GameJolt - http://gamejolt.com/games/arcade/domino-arena/18964/ Domino Arena - http://www.kongregate.com/games/gamieoninc/domino-arena [color=#008080]Paper Cowboys (2013)[/color] http://www.indiedb.com/games/paper-cowboys/ [media][/media] This game marked a turn in my development philosophy. Instead of trying to develop a new concept, I decided to see how fast I could make an online stick-figure western platform shooter. I finished it in 48 total hours. You can play the released version at: http://gamejolt.com/games/platformer/paper-cowboys/15184/ you can see the post-mortem at: https://www.gamedev.net/blog/1606/entry-2256481-paper-cowboys-personal-48-hr-game-jam-post-mortem/ It received enough views and positive comments to motivate me into commissioning Meta3D studios a version with all new papercraft art. In another change of my development philosophy, I put the entire game design in their hands and limited my role only to programmer. This project is still in development at the time of this writing. [color=#008080]Tiltz Tournament (2014)[/color] http://www.indiedb.com/games/tiltz-tournament Skillz, a company developing a mobile multiplayer platform with real cash prizes, approached me about adding part of Tiltz to their lineup. Tiltz Tournament basically takes the Tiltz mini-game of trying to make marbles fall into point slots, and makes it so players can compete online and win money by getting the higher score. I abandoned the project after a quiet release and after I lost personal interest in it. There is no post-mortem for this game. You can try it at: iTunes - https://itunes.apple.com/us/app/tiltz-tournament/id792492685?mt=8 [color=#800000]Downloads / Plays from Nov 30, 2010 - Jan 14, 2015[/color] Tiltz iOS - ~36,400 Android - 44,638 Hyperspace Pinball iOS - ~18,080 Android - 96,922 Desura - 874 Hamster Chase iOS - ~30,600 Android - 47,104 Windows Phone - 9,397 Domino Arena iOS - ~4,870 Android - 2,615 GameJolt - 177 Kongregate - 457 Tiltz Tournament iOS - 127 Paper Cowboys GameJolt - 6,460 Total downloads/plays: ~298,720 Total income: (I'm omitting this number; I will say however that none of my projects have ever made a profit) [color=#800000]Some Lessons I've Learned[/color] Few things can be as fun to a game developer as getting together with some friends and walking to a restaurant and back; all the while discussing ideas for video games. Mentors are powerful allies in helping you learn how to develop games. If you need a powerful game engine, first make sure you really need it and then look at third party solutions before you consider writing your own. Careful planning and doing things right don't guarantee a successful, profitable release. It does however greatly increase your chances. Development is very risky when you do it by yourself; there's nobody else around to error check your plans, your designs and your code. There is always room for another popular game with simple graphics and addicting game play. Learn and use source control early on. You'll thank yourself later. If your goal is to make money, design the monetization model for your game as early as you start designing the game. Graphics are great for getting attention; game play is great for keeping it. Never underestimate the success of a game that users can mod. Set goals and limits. Decide the project must be done in X months. Decide there will be between Y and Z levels, and don't be afraid to narrow things down along the way. Avoid feature creep. It increases development time and takes time away from your more exciting future projects. When you contract work out to other studios, they become an extension of your core team. Treat them with immediacy, courtesy and respect. A professional is just that because they make the difficult look easy. Maintain a news feed for your projects early on. More exposure time means more time to get new readers, and more of a chance for big media sources to take notice and write about your game. Try to make every news update count. A long string of very minor updates can bore readers and clog up news feeds which may include more important things like release announcements from other studios. Maintaining a developer journal with how-to's and fix-it's is a good thing. It helps other developers when they're stuck with the same problems you had, helps you track your knowledge growth, and can even make you new friends. Don't ignore insightful critics. They want your game to be better and cared enough to write about how to do it. Envy of other developers' successes can take fun and feelings of accomplishment away from game development. Discouragement and frustration will keep you from learning important lessons they may share, and make you think you're not as good as you are. Don't work hard; work smart. If you need a tool that has already been developed, use that one. If your stuck on a problem or your project is moving too slowly, ask for help. Make sure your Android apps request only the permissions they need. A developer should eventually outgrow the "code-and-fix" process into something more structured and fit for large projects. It's more challenging to find an audience with an original and untested concept than it is with a proven concept or clone of another game. Insanity is indeed doing the same thing over and over and expecting different results. The learning never stops. [color=#800000]The Future[/color] Although most of my releases were for mobile platforms, I've wanted to release a game on Steam for a long time. I'm working closely with Meta3D studios to try to make it happen with a papercraft version of Paper Cowboys. They are doing not only the art, but also the game design this time. My roles are now "programmer" and "guy who has the final say-so on certain questions." I still have an itch to make a sandbox simulation where people can create Rube Goldberg machines together online. It goes back to my favorite project Dominoze which I think could even be remade as a mobile puzzle game with the right team and taking into consideration all the lessons learned from the original. Lastly I'd like to see if Field of Heroes has a future in a collaboration with another studio. I have a general idea of how I want the game to work, and it's definitely not something I want to design or program alone. [color=#800000]Special Recognitions[/color] Eric Barth, Jeff Gordon, Jordan Pelovitz, Yisroel Goldstein, Helder Gomes, Russ McMackin, Bryan Taylor, and Jarien Strutts all helped me with art and level designs in Dominoze, so thanks again to them. Mick Rippon donated numerous songs to the project, and thanks also go to George Toderici for all the advice and web links that helped me with video engine development! The Hyperspace Pinball playfield was also modeled by Jeff Gordon. I commissioned Meta3D studios and NovyPR to do the artwork and release blast for Hamster Chase respectively; thanks to them for a great job! [color=#800000]Links[/color] Full quality images of all the game thumbnails in this article can be found at http://www.gamieon.com/blogimages/10/ My homepage: http://www.gamieon.com Contact me at: http://gamieon.com/contact My portfolio at IndieDB: http://www.indiedb.com/members/gamieon/ Development blog #1: http://www.indiedb.com/members/gamieon/blogs Development blog #2: https://www.gamedev.net/blog/1606-gamieons-journal/ Development blog #3: http://gamasutra.com/blogs/ChristopherHaag/918299/ Facebook: https://www.facebook.com/Gamieon Twitter: @gamieon LinkedIn: https://www.linkedin.com/pub/christopher-haag/7/650/4a2/ Develteam: http://www.develteam.com/Developer/Gamieon
  5. [color=rgb(128,0,0)]The Issue[/color] I'm developing an online soccer game for UE4 which you can get from http://github.com/Gamieon/UBattleSoccerPrototype for now. During game play, the soccer ball can be in one of two states: Freely moving; or in possession. When freely moving, the ball moves by physics simulation. When in possession, the ball is always in front of the possessing character. I noticed during online testing that the ball position and velocity on the client instances would deviate from the server when freely moving. Thinking I was doing something wrong with replication, I went into the editor and tried every combination of replication flags to fix it to no avail. Some Googling on the matter did not reveal a solution. [color=rgb(128,0,0)]The Solution[/color] I resolved to just deal with the issue myself in the same way I did in my Unity projects using lessons from https://developer.valvesoftware.com/wiki/Source_Multiplayer_Networking . The server would simulate ball physics, and the clients would constantly be fed the ball orientation from the server. The clients would use interpolation/extrapolation to smoothly move their instance of the ball to where the server says it should be. [color=rgb(128,0,0)]Physics Simulation[/color] On the server, the soccer ball physics are simulated and collision detection handled when the ball is not in possession. On clients I ensure the physics are never simulated and that collision detection is always off like so:/** This occurs when play begins */void AMagicBattleSoccerBall::BeginPlay(){ Super::BeginPlay(); if (Role < ROLE_Authority) { // The server manages the game state; the soccer ball will be replicated to us. // Physics however are not replicated. We will need to have the ball orientation // replicated to us. We need to turn off physics simulation and collision detection. UPrimitiveComponent *Root = Cast(GetRootComponent()); Root->PutRigidBodyToSleep(); Root->SetSimulatePhysics(false); Root->SetEnableGravity(false); SetActorEnableCollision(false); } else { // Servers should add this soccer ball to the game mode cache. // It will get replicated to clients for when they need to access // the ball itself to get information such as who possesses it. AMagicBattleSoccerGameState* GameState = GetGameState(); GameState->SoccerBall = this; }} [color=rgb(128,0,0)]Replication[/color] There are three ball properties that must be replicated: Orientation - This is the position and rotation of the ball Velocity - This is used for extrapolation. If the server is slow to replicate data, the client should be able to predict where the ball is going while waiting for more data to come in. Timestamp - The other properties require a context in time for proper interpolation/extrapolation. Sure the ball was at XYZ...but when was it there? I created a USTRUCT with these properties which I call FSmoothPhysicsState.USTRUCT()struct FSmoothPhysicsState{ GENERATED_USTRUCT_BODY() UPROPERTY() uint64 timestamp; UPROPERTY() FVector pos; UPROPERTY() FVector vel; UPROPERTY() FRotator rot; FSmoothPhysicsState() { timestamp = 0; pos = FVector::ZeroVector; vel = FVector::ZeroVector; rot = FRotator::ZeroRotator; }}; The ball has a FSmoothPhysicsState which I define as such: /** The soccer ball orientation on the server */ UPROPERTY(ReplicatedUsing = OnRep_ServerPhysicsState) FSmoothPhysicsState ServerPhysicsState; UFUNCTION() void OnRep_ServerPhysicsState(); and each client tracks the last twenty states (defined as PROXY_STATE_ARRAY_SIZE) in the replication function: void AMagicBattleSoccerBall::OnRep_ServerPhysicsState(){ // If we get here, we are always the client. Here we store the physics state // for physics state interpolation. // Shift the buffer sideways, deleting state PROXY_STATE_ARRAY_SIZE for (int i = PROXY_STATE_ARRAY_SIZE - 1; i >= 1; i--) { proxyStates = proxyStates[i - 1]; } // Record current state in slot 0 proxyStates[0] = ServerPhysicsState; // Update used slot count, however never exceed the buffer size // Slots aren't actually freed so this just makes sure the buffer is // filled up and that uninitalized slots aren't used. proxyStateCount = FMath::Min(proxyStateCount + 1, PROXY_STATE_ARRAY_SIZE); // Check if states are in order if (proxyStates[0].timestamp < proxyStates[1].timestamp) { UE_LOG(LogOnlineGame, Verbose, TEXT("Timestamp inconsistent: %d should be greater than %d"), proxyStates[0].timestamp, proxyStates[1].timestamp); } } [color=rgb(128,0,0)]Timestamps[/color] I previously wrote that the replicated properties require a context in time. Though clients gets server timestamps, a client's current time may not be exactly the same time as the server's. The clients need to know the server's time throughout the game for proper interpolation/extrapolation. To accomplish this, the client does the following: Get the server's time Calculate the difference between the server's time and its own time, and stores it in memory Any time the client needs to know the server's time, the client will get its own time and add the value from step 2 to it. I'll expand on these steps here: [color=#800000]Step 1[/color] The client gets its own system time and stores it in a variable we call "Tc" The client sends an RPC to the server requesting the server's system time The server gets the client RPC. The server then gets its own system time, and responds to the client with that value. The client gets the server RPC and stores the value in "Ts" Immediately after that, the client gets its own system time again, subtracts "Tc" from it, and stores the result in "Tt" So now we have three values: Tc - The system time of the client when it sent the RPC request for step 1 to the server Ts - The system time of the server when it received the RPC request from step 1 Tt - The total length of time it took for the client to get the server's time [color=#800000]Step 2[/color] Ts was the server's time when it received the RPC; so at the moment the client gets it, the time on the server is actually Ts + (the time it took to send Ts to the client). I'm going to estimate the time it took to send Ts to the client as Tt/2 since Tt is the duration of the entire two-RPC exchange. Therfore at time Tc, the time on the server was approximately (Ts - Tt/2). I'll repeat myself because this is important: Therfore at time Tc, the time on the server was approximately (Ts - Tt/2). Now that we know this, we can calculate the difference between the server time and client time, and store it in a new value we call "Td" Td = (Ts - Tt/2) - Tc [color=#800000]Step 3[/color] Now that we know Td, we can calculate the server's approximate time. Since: Td = (Ts - Tt/2) - Tc we can add Tc to both sides: (Ts - Tt/2) = Tc + Td and interpret the equation to mean: The server time = The client time + Td Here are some relevant snippets from my implementation: /** Gets the current system time in milliseconds *//* static */ int64 AMagicBattleSoccerPlayerController::GetLocalTime(){ milliseconds ms = duration_cast< milliseconds >( high_resolution_clock::now().time_since_epoch() ); return (int64)ms.count();}void AMagicBattleSoccerPlayerController::BeginPlay(){ Super::BeginPlay(); // Ask the server for its current time if (Role < ROLE_Authority) { timeServerTimeRequestWasPlaced = GetLocalTime(); ServerGetServerTime(); }}bool AMagicBattleSoccerPlayerController::ServerGetServerTime_Validate(){ return true;}/** Sent from a client to the server to get the server's system time */void AMagicBattleSoccerPlayerController::ServerGetServerTime_Implementation(){ ClientGetServerTime(GetLocalTime());}/** Sent from the server to a client to give them the server's system time */void AMagicBattleSoccerPlayerController::ClientGetServerTime_Implementation(int64 serverTime){ int64 localTime = GetLocalTime(); // Calculate the server's system time at the moment we actually sent the request for it. int64 roundTripTime = localTime - timeServerTimeRequestWasPlaced; serverTime -= roundTripTime / 2; // Now calculate the difference between the two values timeOffsetFromServer = serverTime - timeServerTimeRequestWasPlaced; // Now we can safely say that the following is true // // serverTime = timeServerTimeRequestWasPlaced + timeOffsetFromServer // // which is another way of saying // // NetworkTime = LocalTime + timeOffsetFromServer timeOffsetIsValid = true;}/** Gets the approximate current network time in milliseconds. */int64 AMagicBattleSoccerPlayerController::GetNetworkTime(){ return GetLocalTime() + timeOffsetFromServer;} I'm treating Td as a constant in my implementation. I don't expect the server and client clocks to be running at paces different enough to become significant in the time it takes to finish a game. I also don't want Td to change because the ball movement implementation expects time to always be moving forward instead of going back and forth every so often. You may also wonder "Why do this from APlayerController and not the ball?" Look at these requirements for clients sending RPC's to the server from https://docs.unrealengine.com/latest/INT/Gameplay/Networking/Replication/RPCs/index.html : They must be called from Actors. The Actor must be replicated. If the RPC is being called from server to be executed on a client, only the client who actually owns that Actor will execute the function. If the RPC is being called from client to be executed on the server, the client must own the Actor that the RPC is being called on. The client does not own the soccer ball, thereby failing requirement 4. The client however owns their player controller, and that object meets all the criteria. Client Movement During game play the client will get a stream of ball properties from the server. A critical thing to remember is that those properties are always out-of-date because it takes time for them to get from the server to the client. On the client, the ball is perpetually "catching up to where it is on the server." To make the ball do this smoothly, we use interpolation and extrapolation like so:/** Simulates the free movement of the ball based on proxy states */void AMagicBattleSoccerBall::ClientSimulateFreeMovingBall(){ AMagicBattleSoccerPlayerController* MyPC = Cast(UGameplayStatics::GetPlayerController(GetWorld(), 0)); if (nullptr == MyPC || !MyPC->IsNetworkTimeValid() || 0 == proxyStateCount) { // We don't know yet know what the time is on the server yet so the timestamps // of the proxy states mean nothing; that or we simply don't have any proxy // states yet. Don't do any interpolation. SetActorLocationAndRotation(ServerPhysicsState.pos, ServerPhysicsState.rot); } else { uint64 interpolationBackTime = 100; uint64 extrapolationLimit = 500; // This is the target playback time of the rigid body uint64 interpolationTime = MyPC->GetNetworkTime() - interpolationBackTime; // Use interpolation if the target playback time is present in the buffer if (proxyStates[0].timestamp > interpolationTime) { // Go through buffer and find correct state to play back for (int i=0;i
  6. Yesterday my game started crashing out of the blue. The output window had content that resembled this snippet from https://answers.unrealengine.com/questions/132817/attachment-replication-crashes-client-in-451.html : [ 79]LogOutputDevice:Warning: Script Stack:Actor.OnRep_AttachmentReplicationAssertion failed: !bRegistered || AttachParent->AttachChildren.Contains(this) [File:D:\BuildFarm\buildmachine_++depot+UE4-Releases+4.5\Engine\Source\Runtime\Engine\Private\SceneComponent.cpp] [Line: 903] Attempt to detach SceneComponent 'Default Root' owned by 'BP_Bow_C_1' from AttachParent 'CharacterMesh0' while not attached. UE4Editor.exe has triggered a breakpoint. In my case I had recently removed a component from my player pawn, so I think that was related. I double checked that things were running fine in the editor and that the pawn looked legit. I also made sure nothing more suspicious than usual appeared in the output log. Thinking the game and editor were somehow "out of sync" I started trying random things like rebuilding all from VS2013. It was the act of cooking the content in the editor, however, that made the problem go away. So if you suddenly get random errors in DebugGame mode, try cooking the content from the editor to fix it.
  7. Though I'm still new to the Unreal Editor and behavior trees, I wanted to create a primitive soccer simulation for a game I'm prototyping. You can get the code in its current form at: https://github.com/Gamieon/UBattleSoccerPrototype [color=#b22222]Getting Started[/color] The first part of my journey was learning how "Blueprints" work in the Unreal Editor. I consider a blueprint to be a graphical representation of code. By graphical I mean both pixels-on-the-screen and boxes-connected-to-other-boxes-by-pointy-arrows. You can learn more about how they work at: https://docs.unrealengine.com/latest/INT/Engine/Blueprints/index.html The second part of my journey was learning how "Behavior Trees" work. Buried in Google search results full of complicated papers and tutorials that blew my mind, I managed to find this little gem: http://www.indiedb.com/groups/indievault/tutorials/game-ai-behavior-tree That article clicked with me and I felt like I understood the basics after just one read. [color=#b22222]Setting the Rules[/color] My first step into creating a soccer simulation was to establish a purpose and basic rules for the simulation: The objective is to get the soccer ball into the enemy goal. Each team has eleven players on the field at a time. Each player is confined to an area on the field which I call "action zone." Each player's purpose is to contribute to the objective for their team. A player's only interaction with the soccer ball is to kick it into the goal or to a teammate. Seen here is the reference I chose for assigning field positions for a soccer game http://www.sportspectator.com/fancentral/soccer/soccer_diagram.gif [color=#b22222]Entities[/color] With the rules established, I made a list of the different entities on the field that need to be tracked by a single player: Ball Enemy goal Teammates Enemy players Note the absence of the role of the friendly goal. After several failed attempts at developing the behavior tree, I decided to create a tree that was to be used by every player; everyone from the goalie to the offensive players. I'm not going for perfect; I'm going for simple until I get better at this. Since none of my tree logic factors in the friendly goal, I'm not counting it as an entity here. [color=#b22222]Binary Decision tree[/color] I'm new to behavior trees but not to basic problem solving. After several failed attempts, I came up with a simple decision tree that I could apply to every player on the field: [code=nocode:0]- Is the ball in a goal? Yes - Is it in your goal? Yes A. Celebrate No B. Shrug No - Do you possess the ball? Yes - Are you close to the goal? Yes - Is there an obstacle in the kick line? No C. Take a shot Yes - Is it an enemy? Yes - Is another nearby teammate not close to any enemies and unobstructed? Yes D. Pass the ball toward the teammate No E. Run around enemy toward goal No F. Run around obstacle toward goal No - Is another teammate in front of you closer to the goal, not close to any enemies and unobstructed? Yes G. Pass the ball toward the player No H. Run toward goal No - Does a player on your team have possession of the ball? Yes - Are there enemies within a close distance to the player? Yes I. Pursue enemy closest to player No J. Run in parallel direction to friendly possessor up to the goal No - Does an enemy possess the ball? Yes K. Pursue enemy No - Is an enemy in the line between you and the ball? Yes L. Pursue obstructing enemy No M. Pursue ball Notice how the farther down in the tree you go, the farther away you are from the purpose of the simulation which is to get the ball into the enemy goal. Here's a summary view of it in reverse order; note how it generally follows the progression of a soccer game: Go after the ball Pursue the enemy who possesses the ball Have the team maintain ball control and work with teammates to get the ball to the goal Kick the ball into the goal Celebrate the goal [color=#b22222]What Does "Pursue" Mean?[/color] When a player pursues the ball, all they're doing is running to it. Once the player overlaps the ball's collision sphere, the game assigns them possession and the ball will always be in front of their feet no matter how they turn or run. The only way that a player can lose possession is if they or an enemy player kicks the ball away. When a player pursues an enemy, all they do is run up to them and kick the ball away if they have possession. As I plan for this game to involve on-field fighting ala League of Legends or Diablo 3, I'm purposefully not doing anything more with player pursuits until I design the battle system. [color=#b22222]Leaf actions (tasks)[/color] Once I had a decision tree I was content with, I turned my attention to the leaf actions in the tree and grouped them by purpose: [code=nocode:0]1. GoalFanfare (Is the ball in a goal?) A. Celebrate B. Shrug2. ScoreGoal (Do you possess the ball?) C. Take a shot D. Pass the ball toward the teammate E. Run around enemy toward goal F. Run around obstacle toward goal G. Pass the ball toward the player H. Run to goal3. DefendPossessor (Does a teammate possess the ball?) I. Pursue enemy closest to player J. Run in parallel direction to friendly possessor4. AttackPossessor (Does an enemy possess the ball?) K. Pursue enemy possessor L. Pursue obstructing enemy5. Pursue the ball (Nobody possesses the ball) M. Pursue ball This list helped define the general shape and traversal of my behavior tree: The behavior tree would have a root node with five children below the root. Each child node would have one or more children of its own called "leaf" nodes since they themselves have no children. The tree traversal would start at the root node, then go through the child nodes from left to right until it finds one that is true. From there all of the leaf nodes (which from hereon I'll call "tasks") for that child are traversed from left to right until an action is decided on and performed by the player. [color=#b22222]Spltting up Tasks[/color] Now that my behavior tree prototype was done, I had to make some decisions: Should I split any tasks into new subtrees with their own tasks? What functions do I need to write? Would I share data between tasks? I decided to start by breaking up the tasks by modularity. In Unreal Editor a task is a standalone function which you may create and use in more than one place in a behavior tree. Tasks have access to shared variables stored in a "blackboard" which any task can read or write from. I looked at what tasks I could possibly modularize: [code=nocode:0]1. GoalFanfare (Is the ball in a goal?) A. Celebrate B. Shrug2. ScoreGoal (Do you possess the ball?) C. Take a shot D. ** Pass the ball ** E. ** Run to goal (use Unreal's internal pathfinding to avoid obstacles) ** F. ** Run to goal (use Unreal's internal pathfinding to avoid obstacles) ** G. ** Pass the ball ** H. ** Run to goal (use Unreal's internal pathfinding to avoid obstacles) **3. DefendPossessor (Does a teammate possess the ball?) I. ** Pursue enemy ** J. Run in parallel direction to friendly possessor4. AttackPossessor (Does an enemy possess the ball?) K. ** Pursue enemy ** L. ** Pursue enemy **5. Pursue the ball (Nobody possesses the ball) M. Pursue ball I broke every task with ** in two: One is the modular task that can be used in multiple places in the tree, and the other is the task that calculates what data to give to that modular task. I changed my behavior tree to look like this: [code=nocode:0]1. GoalFanfare (Is the ball in a goal?) A. Celebrate B. Shrug2. ScoreGoal (Do you possess the ball?) C. Take a shot D1. Find a nearby teammate we can pass to D2. ** Pass the ball ** E1. Determine if an enemy is obstructing our route to the goal E2. ** Run to goal ** F1. Determine if a non-enemy actor is obstructing our route to the goal F2. ** Run to goal ** G1. Determine if there is a player closer to the goal we can pass to G2. ** Pass the ball ** H. ** Run to goal **3. DefendPossessor (Does a teammate possess the ball?) I1. Determine if an enemy is near the possessor I2. ** Pursue enemy ** J. Run in parallel direction to friendly possessor4. GetPossession (Ball is still in play but nobody on our team possesses it) K1. Determine if an enemy possesses the ball K2. ** Pursue enemy ** L1. Determine if an enemy is in the way between you and the ball L2. ** Pursue enemy ** M. Pursue ball I needed only two Blackboard variables for passing data from one task to another: KickTargetActor (read by "Pass the ball" and "Take a shot") PursuitActor (read by "Pursue enemy" and "Pursue ball") [color=#b22222]Task Development[/color] I won't bore you with how I developed the blueprint for each task here, but I will say that I tried to keep all of them small and modular where possible. I thought about posting screenshots but that would be like posting random snippets of code with no context. Instead you can see a screenshot of the final behavior tree here. You may notice that the child nodes have "blue things" on them. Those are Unreal Editor-specific elements called "decorators." You can use them as a switch to tell the traversal whether it should iterate through the tasks for that child node. Here's the complete blueprint for my decorator that informs the traversal whether the soccer ball is in a goal: [color=#b22222]Task Testing[/color] While developing tasks I bounced back and forth between blueprinting and testing to make sure all my changes worked. It was after all the tasks were written that I got the idea to write manual "Unit Tests." I would create one scene for each behavior tree traversal to verify it worked through manual observation. This definitely helped because two of the "Unit Tests" revealed bugs that would have been much harder to pin down in a full simulation. Here are some poorly lit screenshots of: A player kicking the ball into the net A player passing to a teammate that is farther away from a row of enemies than another teammate A 2v2 clump of players pursuing the ball I'm aware that in Test-Driven development one is supposed to write unit tests first rather than later...so to all you developers I've offended out there by writing them last: Deal with it! [color=#b22222]Final Product[/color] Here are some screens and a video of more test scenes and a single team on a field. The screens go in progression from the start of the simulation to the end: [media][/media] [color=#b22222]Implementation concerns[/color] In the game the tree traversal always goes through all child nodes instead of stopping at the first one whose decorator returns true. I need to fix that. I created a class inherited from GameMode as a hub for global functions and variables (such as the soccer ball). I suspect I'm not supposed to do this but I don't know a better place for them. In each task I cast the owning actor input variable to an AI controller, then call GetControlledPawn, then cast that result to a BotTeammate to get the actual character on the field. It seems like a lot of work to get access to that character class... I really wanted the human characters and bots to have identical interfaces so I wouldn't have to do "if (bot) {} else if (player) {}" branches, but haven't figured out how yet. The editor recently acquired an incessant desire to rebuild the navigation area every time I start a simulation. It happens after a fresh launch of the editor after I start modifying blueprints. This: https://answers.unrealengine.com/questions/49111/bug-trigger-volume-causes-trace-hit-with-no-collis.html . I resolved my issues by having traces only look for pawns. If I ever decide to add static obstacles to the field I'll have to come back to this. [color=#b22222]What's next?[/color] Now that I'm satisfied I can develop a basic soccer simulation, the next step is to start designing the game as a whole. As I wrote previously, I intend to have opposing players fight each other for ball possession. Fighting may include melee attacks, ranged attacks, and magic. It may involve launching attacks from a distance, or even team tactics where one player freezes an opponent before the other launches a localized lightning storm at them. There's also the matter of letting players accumulate resources to spend on armor, weapon and skill upgrades for themselves and their bot teammates. At first I was thinking a Warcraft 3-like deal where you build structures and kill creeps...but decided it would make more sense to have creeps on the field and possibly special item shops (concession stands) on team sidelines. Regular shops could appear in-between matches. Should I have player leveling? Should I allow for instant action matches where all players start at the same level and no items for a competition of pure skill? There's even the matter of configuring the bot teammates: Team captains could arrange the bot positions in defensive or offensive formations as well as define the aggression level for each bot. Perhaps a team captain would prefer that human players pursue the ball while bots do nothing but attack enemy players for example. I should probably find some people to help me design all this; preferably those who have a lot of experience with MOBA's and balancing.
  8. While in-between major projects that use the Unity game engine, I decided to give the Unreal Editor a spin to see what all the hype was about. For those of you who just want to see the cool stuff first, here's a video of my first playable Gauntlet-like dungeon crawl prototype finished in about twenty five hours of work. [media][/media] The assets were all imported from Unreal's marketplace. All I did was put everything together. [color=#b22222]Getting Started[/color] After getting through the first hour of "I have no idea what is going on" and then watching Unreal's tutorial videos, I started setting little goals for myself to learn how to do things. The first of these was figuring out how to turn off "Realtime" editor rendering since I didn't like my computer fans going full blast while I was idle. The second was to resign myself to do something simple yet neat looking. [color=#b22222]Spotlight Test[/color] My first accomplishment was adding a spotlight to a third-person tutorial scene and having it follow the player character. If the character stood on top of a button, the spotlight would turn off. If they got off, the spotlight turned back on. I briefly thought about developing an evade/infiltrate kind of game, but was more interested in making my own Mineralz/LoL/Tower Defenese game that took place in the old Wild West. Lets just call it "Lone Star Guns." [color=#b22222]Lone Star Guns[/color] The next accomplishment was prototyping the home base for Lone Star Guns. The heart of the base was a campfire. Surrounding the base and taking up most of the playfield would be rocks that you carve out to expand your base and create additional lanes for enemy zombies to come in from (thereby relieving pressure on the default lane). For prototyping purposes, I added only a few rocks around the base. There are also three resources you can have workers "mine" from: Food (crops), wood (trees), and a mine (metal). My interest faded after making a basic inanimate scene; I felt like I was borrowing too many ideas from other games and I wasn't doing any serious development anyway. I then decided to help my friend with a prototype for his version of "Gauntlet" by creating my own prototype first. [color=#b22222]Gauntlet Clone[/color] I set a goal to make my Gauntlet prototype resemble a Diablo 3 level. This was done by creating a landscape and carving out paths in it. I failed to make the landscape look like the bottom half of a cave (I'm pretty sure it wasn't designed for that) so I did a Google search for anything related to the Unreal Editor and caves. Lucky me, I found that Unreal had a Cave Effects demo on their marketplace. It was beautiful, and I wanted those assets in my scene! As I downloaded it I also noticed the Mixamo Animation Pack for character prototyping on the marketplace as well...PERFECT. To keep a long story short: I replaced the landscape with a collection of rocks, added neat looking particle effects and materials all over the place, figured out how to make a flaming sword and put it in a character's hand, make the character throw fireballs, and make enemies spawn and get hit by fireballs. After about twenty-five hours of playing with the Unreal Editor since installing it, I actually had a functional gauntlet-like prototype! I admit I took a real hack-and-slash approach to learning, but I wanted to see what I could accomplish by tinkering with the Unreal Editor at my own pace. [color=#b22222]What's next?[/color] Though I'm tempted to finish developing a game in Unreal, I'll be going back to Unity development once the studio I'm working with finalizes the design document for the new Paper Cowboys. Until then I'll probably do one or more of the following in my free time: Look at what other features UE4 has to offer and put them in my own list with two measures: One for "difficulty level" and one for "coolness factor." Look at UE4's asset importing. I'd like to know more about what file formats it supports and its thought process when importing meshes with textures Learn more about materials and particle systems Learn more about landscape generation Create some basic AI
  9. The focus of this journal entry is on how I integrated In-App purchase capabilities into my Unity game. I'm not going to discuss strategies for earning money from players, or how I used the IAP assets once they were integrated. [color=#b22222]Unity Setup for Zaubersee[/color] I began by purchasing a Windows Phone 8 IAP Unity package by Zaubersee at https://www.assetstore.unity3d.com/en/#!/content/10890 to get things going. Like with other third-party Windows Phone 8 assets I've used, it didn't work out of the box. I also struggled a bit with my interpretation of the documentation. After creating a new Unity test project, importing the asset, and deploying to my Windows Phone, I got this error in the status window of the demo scene: Exception from HRESULT: 0x805A0194 I overcame this error by submitting the test project as a beta app to the Windows Phone Dev Center. I believe the asset developer tried to explain this in the documentation, but I thought that submitting a beta version of the app was merely a suggestion. Here's what I did to build a functional test project: I created a beta version of Hamster Chase on the Windows Phone Dev Center. For the beta version, I made a duplicate of every In-App product that already existed for the main version. I used the same product ID's. I made a new Unity project, imported the In-App purchase asset, and built the project for Windows Phone 8. I opened VS 2013 to the Windows Phone 8 project I built from the previous step, then opened WMAppManifset.xml under the Properties folder. I went to the packaging tab and updated the ProductID and PublisherID values to be those of Hamster Chase Beta. I made a Master build of the app, and submitted it to Windows Phone Dev Center. I made absolutely sure it was a beta, and not an app to be shown in public. About an hour later I got an e-mail from Microsoft with the download link. I went there from my phone, installed the app, and the purchases worked! Cleanup: From my phone, I went to the home screen, then Apps, then Games to find and uninstall the test app. Taking this idea a step further, I opened the VS 2013 project for the real Hamster Chase, set the product ID to be that of the beta app, and then deployed it in debug mode to the device. Since the test app worked well, I figured it would work the first time in my game...but it did not. The app crashed after the app was restored upon me dismissing Microsoft's purchase screen. My guess is that in the onWP8inAppPurchased callback, I wasn't supposed to do stuff with Unity objects like changing TextMesh text values or calling Transform.GetChild(). I proceeded to move all that code into a function called DoStuffAfterPurchaseFinished(), and then I call Invoke("DoStuffAfterPurchaseFinished", 0.2f) within the callback. The crash went away after that. [color=#b22222]Unity Setup for Prime31[/color] I was about to move on entirely when I discovered that Prime31 was offering a free Windows Phone 8 In-App package (while the offer lasted) at http://prime31.com/plugins . I've worked with Prime31's assets before, and I like their brand. I decided to make a test app with Prime31's IAP package. Using the lessons learned from integrating Prime31's WinPhoneAds package and the Zaubersee package; I was able to quickly get a functional Prime31 In-App demo app. I then imported the Prime31 package into the official Hamster Chase app, and tested it. To my surprise, I had the same crash I had with the Zaubersee asset when trying to manipulate GameObjects within the purchase completion callback. As before, the Invoke workaround made the crash go away. [color=#b22222]Conclusion[/color] In the end I got both the Zaubersee and Prime31 assets to work; but I decided to stick with the Prime31 asset primarily out of brand loyalty and the expectation of continued upkeep.
  10. In one effort to monetize Hamster Chase, I wanted to have ads appear in it. It took me a while to decide where to put the ads; the articles at https://inneractive.jira.com/wiki/display/DevWiki/Ad+Placement+Strategy and http://streetfightmag.com/2012/12/14/can-mobile-advertising-be-made-less-annoying/ were helpful with my decision making. I decided to have ads appear briefly at the end of each level, and at random in the hamster cage in the form of a comic strip balloon coming out of a hamster's mouth. [color=#b22222]Ad Portal Setup[/color] I created an AdDuplex account on http://www.adduplex.com . AdDuplex treated me as both someone who wanted ads in their game, and someone who wanted to put their own ads in other games. They wouldn't let me submit my game before it was published. I created a Microsoft pubCenter account on https://pubcenter.microsoft.com . I was able to create one "ad unit" (which is a fancy way of saying "an ad banner to appear in the game" I think), but I had trouble changing it later and adding more ad units. I was informed by an online help desk representative that I couldn't do those things until my game was published. [color=#b22222]Unity Setup for AdRotator[/color] My journey with Ad integration began with AdRotator v2 beta at http://getadrotator.com/adrotator-v2-updated-to-beta-release-of-the-v2-unity-plug-in/ . AdRotator supports multiple ad providers; a list is available at http://getadrotator.com/ . I made a new test project in Unity and imported the AdRotator Unity package into it. The first thing I noticed was the AdRotatorDemo scene had no AdRotator components in it. I figured out which ones belonged, and added them manually. Later on I discovered the menu item to do it: GameObject => Create Other => AdRotator. I clearly glazed right over "this will appear in the game object create menu" in their documentation; but in my defense I'm used to the vendor having the demo scene all set up for me. I was puzzled by the setup: On the pubCenter and AdDuplex portals, I could choose the size of my ads. In the AdRotatorManagement script was a dropdown where I could choose the size of the banners; but none of the selections matched the sizes I assigned in the portals! I decided to just leave all the component settings unchanged. My attention turned to defaultAdSettingsWP8. It's the file in the Unity project where you set up all your ad portal identities. I discovered I could comment out the elements corresponding to providers that I did not sign up for, and they would not be included in the rotation of served ads. I like how the developer filled everything in with demo values; it made it easier to confirm that the values I would replace them with were formatted correctly. I then proceeded to Build and Run the project on my phone...and of course, no ads appeared. My first inkling was that I glazed over something else important in the documentation. I opened the VS 2013 solution and ran it again. I got at least one System.IO.FileNotFound exception. Between seeing that, and revisiting the documentation, I found that I was missing several assemblies. I made sure to have the following References in my project: AdDuplex.WindowsPhone AdRotator AdRotator.Core Microsoft Advertising SDK for Windows (Silverlight) Microsoft.Advertising.Mobile Microsoft.Advertising.Mobile.Common Microsoft.Advertising.Mobile.UI I deployed the project to my phone again, and the only ads that would appear were test ads for AdDuplex. The debug log reported once that it successfully got a pubCenter ad, but I don't think it ever appeared. Subsequent attempts to get pubCenter ads failed with no error description ever since. It could have been that pubCenter simply had no ads available at that moment; I never figured out what happened in the end. [color=#b22222]Unity Setup for Prime31[/color] I was about to settle for only using AdDuplex ads when I discovered that Prime31 was offering a free Windows Phone 8 Unity package for pubCenter ads (while the offer lasted) at http://prime31.com/plugins . I've worked with Prime31's assets before, and I like their brand. I grabbed it right away and built a test app with it. There was one issue I had trying to get it working: I got this message trying to complete a purchase: System.UnauthorizedAccessException: Invalid cross-thread access. at MS.Internal.XcpImports.CheckThread() They quickly gave me a resolution which you can read at http://support.prime31.com/13412/windows-phone-ads-fix-prime31-uihelper-has-null-dispatcher?show=13418#a13418 . The test app worked fine after that; I was able to see pubCenter test ads. I understand that a provider doesn't want to feed live ads to a developer's test environment, but one of my peeves as a developer is not seeing a component work in live mode before the project is published. Right after the game was published, the test ads were replaced with live ads automatically. [color=#b22222]Conclusion[/color] In the end, I decided to run with Prime31 and pubCenter ads. I'd like to see AdRotator 2 mature a bit more before I consider using its capabilities. I don't know which ad provider is the best for my game, so I'm going to start with pubCenter. I won't be able to measure its effectiveness on Hamster Chase until the press release has been out for a while and more than a handful of people are playing it. Eventually I may try switching providers or my integration strategy to see if I can improve revenue.
  11. I wanted to add Facebook integration to Hamster Chase for Windows Phone 8 in an effort to have players get word out of the game through social media in exchange for a reward. A search on Google led me to Chillster's Facebook SDK at http://forum.unity3d.com/threads/facebook-sdk-for-unity-plus-windows-phone-8.217907/ . It was free and also fairly new. I had a fair bit of trouble getting it to do what I wanted because of limitations I wouldn't accept, and because of my interpretations of the instructions. Ultimately I got it working. Here's a walkthrough I would have presented: [color=#a52a2a]Facebook Setup[/color] 1. First you need to add your Windows Phone 8 app to your Facebook developer dashboard. Do this by going to https://developers.facebook.com/ and clicking on Apps at the top. If your app doesn't exist, use Create a New App and fill the proceeding form out. 2. If your app already exists (like you wrote your game for iOS/Android first), you still need to set it up. On the left side of the app's dashboard page on Facebook is a menu item for Settings. Click on it. Hidden beneath your existing platform listings is a "+ Add Platform" button. I swear I went to this page half a dozen times and never saw that button. Click on it and add your app product ID (which you get from your Windows Phone Dev Center account) to the second line. Remove the dashes and make sure you don't have curly brackets. [color=#a52a2a]Phone Setup[/color] I had to install the Facebook app on my phone before I could use Facebook integration. [color=#a52a2a]Unity Setup[/color] 1. Add Chillster's Facebook SDK asset to your Unity project (or better yet make a test project). 2. Look up at your Unity menu bar (File, Edit, Assets...). There should now be a Facebook menu. Go there and choose "Edit Settings" and fill those out. 3. Now go to the link I mentioned at the start of this section (http://forum.unity3d.com/threads/facebook-sdk-for-unity-plus-windows-phone-8.217907/ ), and read Chillster's official documentation for getting set up starting with step 2.1. You've already done step 2.3, so you can skip that step. [color=#a52a2a]Unity Usage[/color] My goal was to make it so users could send app requests to friends which invited them to download my game. So when I found this in the code comments: [color=#0000cd]"... throw new UnityException("There is no Facebook AppRequest on Windows Phone 8"); ... "[/color] I thought it was just an asset limitation. For a while I tried using FB.API() to get the job done starting with getting a friends list. I simply could not get that to work until I finally called "FB.API("me\friends"...)" (note the direction of the slash), and making sure to include the "user_friends" permission at the login. Then I tried a number of ideas to send an app request by using FB.API(); all failed. I ultimately settled for just doing a post on a player's feed by calling: var wwwForm = new WWWForm();wwwForm.AddField("link", "http://www.gamieon.com/hamsterchase");wwwForm.AddField("linkName", "Hamster Chase for Windows Phone 8!");wwwForm.AddField("linkCaption", myCaption);FB.API("me/feed", Facebook.HttpMethod.POST, fb_OnFeedComplete, wwwForm); It doesn't show the link name or caption in my feed; but I'm going to leave those in anyway in case they someday appear through either asset or Facebook-side updates. [color=#a52a2a]Conclusion[/color] I ultimately released Hamster Chase with Facebook integration. It works for posting to Facebook feeds, and it was nice to see it work without having to publish the game first (unlike other assets I integrated with). I'm sure it won't be long before future versions of Facebook integration assets are released with easier setups and more features; including app requests.
  12. In a previous journal entry I described my experiences converting my Unity iOS/Android app to Windows Phone 8. Here I continue on about my experiences with combating app slowness as well as setting app properties from the generated VS 2013 project. [color=#a52a2a]Transparency Performance Issues Resolved[/color] Experience has taught me that iOS is efficient with transparent rendering; and Android not as much. When the Unity profiler reported that transparent rendering was very slow for Hamster Chase (~18ms per frame) in a very basic scene with a low polygon count, I was about to give up hope of porting any of my games entirely entirely until I discovered the problem: I wrote a component that would smoothly fade meshes in and out. When they were invisible their renderers were enabled, but the main color's alpha value was set to zero. This turned out to be a huge performance killer as perhaps a quarter of the scene, consisting of prompts and menus, was this way. Upon changing my component to disable the renderer when the alpha value was zero, the major performance problems went away. I still think it should have run faster regardless, but I digress. Wondering how well another game of mine would perform, I converted and deployed a Master build of "Hyperspace Pinball" to my Windows Phone. It makes heavy use of particle systems and transparent rendering, so I expected it to get a low frame rate. After playing for just two minutes, I confirmed this to be so. It was a far cry from the very fast frame rates of every other pinball game I've ever played. I did not run the profiler or look into it further; perhaps someday I will. Having eliminated the performance issue in Hamster Chase, all that was left to do before releasing it was to set up the splash screen, app icon, and app properties (app ID, permissions, etc.); and integrate third-party Facebook, In-App Purchase and Advertising assets. [color=#a52a2a]Splash Screen and Icon[/color] I was not able to configure the splash screen from my version of Unity, but I did find where it was located in the VS 2013 project. In the root of the project are five files: SplashScreenImage.jpg SplashScreenImageLandscapeLeft.jpg SplashScreenImageLandscapeRight.jpg SplashScreenImagePortrait.jpg SplashScreenImagePortraitUpsideDown.jpg As my game is landscape, I replaced the first three files with my splash screen image file. I made sure my image retained the same width, height, and orientation as the one I overwrote. In the Assets folder I found ApplicationIcon.png, and replaced it with a png of the Hamster Chase icon with equal dimensions as well. After subsequent project builds from Unity, none of my changes got undone. [color=#a52a2a]App Settings[/color] Further poking around in the VS 2013 project led me to WMAppManifest.xml under the Settings folder. Here I found that I could set the app capabilities, requirements, product ID and publisher ID. Those values also remained untouched by subsequent Unity builds. [color=#a52a2a]Facebook, In-Apps and Ads[/color] Integrating each third-party component to make Hamster Chase more feature-rich was a unique challenge. I plan to write about them in subsequent journal entries with the aim of making them helpful references to other developers.
  13. A couple weeks ago, a friend tipped me off to a free Microsoft Unity workshop that was held in Orlando, FL. Though reluctant at first, I decided to go. I needed to get out of my lone wolf developer cave, meet other folk, and see what Microsoft could do to help me port my mobile games to Windows Phone 8. Long story short, it was a good experience and I was on my way to getting those games ported. Having released Hamster Chase for iOS and Android already, I figured getting it deployed to my Windows Phone would be a breeze...but it wasn't. [color=#b22222]Upgrading the project to Unity 4[/color] The first step was to make a branch of Hamster Chase for Unity 4. Right away I got a number of new compiler warnings; mostly about the GameObject.active setter being deprecated for GameObject.SetActive(). After fixing and re-testing all of the related code, I found another issue: in Unity 3.x, I would set certain objects to be "static" (motionless) objects at runtime when they wouldn't move, and then unset the static flag before they were to move again. I thought this would provide an optimization in rendering, and possibly with the physics too. In unity 4.5 at least, it would seem that once an object is static, it is always static and would never move again. I fixed this by simply never setting the static flag in the first place. Those were the only two issues I dealt with during the upgrade process. With all the compiler warnings and static behavior fixed, I was ready to change the platform to Windows Phone 8. [color=#a52a2a]Fun With Frameworks[/color] After changing the platform, I attempted to build the project. Right away, I got errors related to my Prime31 Android & iOS plugins. Prime31 is an organization that develops plugins which enable developers to implement social network check-ins, ads and in-app purchases. It's a shame they also don't include a 'Monetization for dummies' manual with those plug-ins, but I digress. I wish I had retained the exact error messages for others to find on Google, but alas, I didn't think of it at the time. I ultimately fixed the errors by deleting all of the Prime31 plug-ins, and changing my side of the code to only look for them in the iOS and Android platforms. My next build attempt gave me these more memorable error messages: [color=rgb(17,17,17)][font=Helvetica][background=rgb(254,254,254)]Error: 'WriteAllBytes' is not a member of 'System.IO.File'[/background][/font][/color] [color=rgb(17,17,17)][font=Helvetica][background=rgb(254,254,254)]Error: type `System.Xml.XmlDocument` doesn't exist in target framework.[/background][/font][/color] Error: `System.Security.Cryptography.MD5CryptoServiceProvider` doesn't exist in target framework There are three ways you can deal with these: In Unity, go to File => Build Settings => Player Settings. In your Inspector window, expand Other Settings, and change your API Compatibility Level to .NET 2.0. [color=#ff0000](Note: I had already fixed System.IO and my MD5CryptoServiceProvider compatibility issues before trying this, but I think it should work)[/color] Find an existing implementation that you can copy into your project. Write your own code to replace the functions provided by those frameworks. I opted to do 3 to keep the binary size at a minimum; of course that took a fair bit of time to do. [color=#a52a2a]A "Successful" Build?[/color] After fixing all that, I managed to get Hamster Chase to deploy to an emulator. I noticed the splash screen was of Unity and not from the game; nor was there a setting to change the splash screen in the Player settings. I hope to deal with that from the Visual Studio project itself later. I was happy... ...and then my happiness was shattered once I got it deployed onto my new Nokia Lumia 521. I was barely getting 15 FPS on the device! The game was choppy, overlays that should have faded in instead went straight to fully visible, and popup menu animations were even choppier than the game. After attaching the Unity profiler to my phone via IP address (it takes like 45 seconds to connect), I pinned the main problem to a GameObject.FindObjectsOfType being called in every frame. After fixing that, the game was slightly faster. It was still far from the silky smoothness I see in most mobile games. The main culprit is now transparent rendering which is taking up over half the workload each frame. In the main menu alone it takes 18ms per frame. I had the same problem with Hyperspace Pinball; and it took months to optimize it just enough to even be releasable. I am not going through that again. Thinking the problem was that the VertexLit shader was slow on mobile, I decided to try my hand at writing two transparent mobile shaders: Unlit shader:Shader "Mobile/Transparent/Unlit" { Properties { _MainTex ("Base (RGB) Transparency (A)", 2D) = "" {} } SubShader { Pass { // Only render pixels with an alpha larger than 50% AlphaTest Greater 0.5 SetTexture [_MainTex] { combine texture } } }} Simple lit shader:Shader "Mobile/Transparent/Simple" { Properties { _MainTex ("Base (RGB) Transparency (A)", 2D) = "" {} _IlluminCol ("Self-Illumination color (RGB)", Color) = (1,1,1,1) } SubShader { Pass { // Only render pixels with an alpha larger than 50% AlphaTest Greater 0.5 SetTexture [_MainTex] { constantColor [_IlluminCol] combine texture * constant } } }} After using these on the most prominent main menu objects, I didn't get a visible performance gain. [color=#a52a2a]What's next?[/color] It occurs to me that Hamster Chase was developed before Unity had Sprites and Sprite Renderers. In Hamster Chase, the existing "Sprites" are rendered using regular MeshRenderers and VertexLit shaders on simple four-corner squares. I think if I changed my "Sprites" to be actual Unity Sprites, things would render faster. I'll have to ponder if and how I would accomplish that because it could easily be a ton of work. I still have trouble wrapping my head around the fact the game is this slow as is using out of the box shaders on such a sophisticated piece of equipment. It's notably faster on both iOS and Android; what am I missing here? I hope to find a solution and publish a follow-up journal entry on it.
  14. I finally got Paper Cowboys to a playable alpha state. I originally wanted to wait until it was much more polished; but I decided this was a good way to find people interested in helping me finish the game through constructive feedback, help with PR and play testing. And besides, if something is playable, why not post it? Paper Cowboys is available via Unity web player at http://www.playpapercowboys.com A couple points of interest: WSAD to move, Left Mouse to fire, Right Mouse for special weapon attacks if you have any. There are only three levels right now. After you beat the third one, the game just stops. Shoot barrels for random special weapon drops. Use Right Mouse to use. Shoot hats off enemies and pick them up! Shoot enemies for ammo and weapon upgrades. Don't forget to go into Options from the main menu and set your profile name and color It's multiplayer; so try to play with a friend! Hopefully that works...remember I'm a team of one and this has never been tested by the public until now. Contact me if you'd like to be a play tester, co-designer or would like to help with PR. Here are some new screenshots I took; hopefully these will be among the last where there's only one player (myself).
  • 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!