Jump to content
  • Advertisement
  • entries
    7
  • comments
    4
  • views
    1231

About this blog

Dev logs for the war city runner game

Entries in this blog

 

Dev Log #6

Intro So this has been a long time coming so some details might be missing but praise be to source control. Since the last blog I have updated the engine, and done some major code refactoring in the way references are handled. The major game-play update has been ability to move the character and double jump along with new visuals including boost particle animation and end screen in animation. Note I am also starting to add code snippets now with class names at the start mentioned in a comment.   World Config As an infinite runner game the word moves towards the player, the player does not move through the world so this config will maintain configurable data that affects the environment as a whole. Currently that is the speed that the world moves at. using UnityEngine; public class WorldConfig : ScriptableObject { public float worldBaseSpeed = 3; } //PlayerIdle.cs [SerializeField] Transform endMarker; [SerializeField] PlayerData playerData; + [SerializeField] WorldConfig worldConfig; - float worldSpeed = 3; //TODO: this needs to come from config Vector3 idleMoveVector; bool isLeftOfObstacleColliding = false; bool isAtEnd = false; //PlayerIdle.cs void DoIdle() { idleMoveVector = transform.position; - idleMoveVector.x = idleMoveVector.x - (worldSpeed * Time.fixedDeltaTime); + idleMoveVector.x = idleMoveVector.x - (worldConfig.worldBaseSpeed * Time.fixedDeltaTime); transform.position = idleMoveVector; } //Obstacle.cs public Action<Obstacle> OnReachedEnd; [SerializeField] ObstacleConfig config; + [SerializeField] WorldConfig worldConfig; [SerializeField] GameObject endPoint; Vector3 moveVector; //Obstacle.cs void Move() { - moveVector.x = moveVector.x - (config.horizontalSpeed * Time.fixedDeltaTime); + moveVector.x = moveVector.x - (worldConfig.worldBaseSpeed * Time.fixedDeltaTime); transform.position = moveVector; }     Service Locator This is one of my biggest refactoring update for this time the service locator. What it does is simply searches for a MonoBehavior using Find by type and returns its instance in case the instance is not found it has the option to create a new instance. This eliminates the need for singleton or a lot of drag and drop references in the inspector. using System.Collections.Generic; using UnityEngine; using System; namespace Treader { public class ServiceLocator : MonoBehaviour { public static ServiceLocator Instance; Dictionary<object, MonoBehaviour> services; bool canCreateIfNotFound = false; public T GetService<T>(bool createIfNotFound) where T : MonoBehaviour { canCreateIfNotFound = createIfNotFound; if (services == null) { services = new Dictionary<object, MonoBehaviour>(); } try { if (services.ContainsKey(typeof(T))) { T service = (T)services[typeof(T)]; if (service != null) { return service; } else { services.Remove(typeof(T)); return FindService<T>(); } } else { return FindService<T>(); } } catch { throw new Exception(); } } T FindService<T>() where T : MonoBehaviour { T service = GameObject.FindObjectOfType<T>(); if (service != null) { services.Add(typeof(T), service); } else if (canCreateIfNotFound) { service = gameObject.AddComponent<T>(); services.Add(typeof(T), service); } return (T)services[typeof(T)]; } private void Awake() { if (Instance == null) { Instance = this; } } } }     Horizontal Movement This just involves the ability for the player to move horizontally giving the player a lot more versatility to avoid the obstacles. using UnityEngine; public class PlayerHorizontalMovement : MonoBehaviour { [SerializeField] PlayerData playerData; Vector3 moveVector; // Start is called before the first frame update void Start() { moveVector = Vector3.zero; } // Update is called once per frame void Update() { if (Input.GetAxis("Horizontal") != 0) { DoMovement(); } } void DoMovement() { moveVector.x = (Input.GetAxis("Horizontal") * playerData.pPlayerConfig.speed * Time.fixedDeltaTime) + transform.localPosition.x; moveVector.y = transform.localPosition.y; transform.localPosition = moveVector; } } public class PlayerConfig : ScriptableObject { public float jumpForce = 20; + public float speed = 20; public float gravityModifier = 0.5f; }   Falling Once a player gets on top of an obstacle they need to be able to fall off it. To my pleasant surprise the same code that handles the jump for the character also handles their fall after all what is falling if not jumping with 0 force. public class PlayerCollisionEvents : MonoBehaviour { - public Action OnTopOfSomething; + public Action<GameObject> OnTopOfSomething; public Action OnCollideObstacleLeft; public Action OnObstacleCollisionExit; //PlayerCollisionEvents .cs void OnTriggerEnter2D(Collider2D collision) { eventsCollider = collision; if (IsOnTop()) { if (OnTopOfSomething != null) { - OnTopOfSomething(); + OnTopOfSomething(collision.gameObject); } } +using UnityEngine; public class PlayerJump : MonoBehaviour { [SerializeField] PlayerData playerData; bool canJump = true; + bool onTopOfObstacle = false; Vector3 jumpVector; Vector3 fallVector; //PlayerJump.cs private void Start() { playerData.playerCollisionEvents.OnTopOfSomething += HandleOnTopOfSomething; + playerData.playerCollisionEvents.OnObstacleCollisionExit += HandleObstacleCollisionExit; } //PlayerJump.cs + void InitFall() + { + jumpVector = transform.position; + currentForce = 0; + canJump = false; + } //PlayerJump.cs - void HandleOnTopOfSomething() + void HandleOnTopOfSomething(GameObject topOfObject) { - Debug.Log("HandleOnTopOfSomething"); canJump = true; playerData.mainAnimator.SetBool(PlayerAnimatorKeys.GROUNDED, true); + onTopOfObstacle = topOfObject.GetComponent<Obstacle>() != null; + } + + void HandleObstacleCollisionExit() + { + if (onTopOfObstacle) + { + InitFall(); + } }     Double Jump and Jump Boost Effect Double jump simply involves checking if a double jump is done or not and allowing the player to initiate a jump again. Now the booster was a little more tricky because it needs to stay in scene at the position the double jump triggered run the course of its animation and then go away. Now the simple solution would be to destroy the booster each time it has run its course but that seemed a little wasteful so I put the booster in a pool each time its used and reuse the same booster again and again. If a double jump is initiated before the booster has run its course then a new booster in initiated and the pool keeps track of all these boosters.   Double Jump //PlayerJump.cs [SerializeField] PlayerData playerData; bool canJump = true; + bool doubleJump = true; bool onTopOfObstacle = false; Vector3 jumpVector; //PlayerJump.cs void InitJump() { jumpVector = transform.position; currentForce = playerData.playerConfig.jumpForce; - canJump = false; + + if (!doubleJump) + { + canJump = false; + } + else + { + doubleJump = false; + } } void InitFall() { jumpVector = transform.position; currentForce = 0; - canJump = false; + //canJump = false; + doubleJump = false; } //PlayerJump.cs void FixedUpdate() { - if (!canJump) + if (!canJump || !doubleJump) { DoJump(); } //PlayerJump.cs void HandleOnTopOfSomething(GameObject topOfObject) { canJump = true; + doubleJump = true; playerData.mainAnimator.SetBool(PlayerAnimatorKeys.GROUNDED, true); onTopOfObstacle = topOfObject.GetComponent<Obstacle>() != null; } void HandleObstacleCollisionExit() { - if (onTopOfObstacle) + if (onTopOfObstacle && canJump && doubleJump) { InitFall(); }   Booster Effect using System.Collections.Generic; using UnityEngine; public class BoosterPool : MonoBehaviour { [SerializeField] Booster boosterTemplate; [SerializeField] List<Booster> boosterPool; Vector3 boosterPosition; // Start is called before the first frame update void Start() { boosterPool = new List<Booster>(); boosterPosition = Vector3.zero; } public void TriggerBoost(Vector3 position) { Booster booster = null; boosterPosition = position; booster = FetchFromPool(); if (booster == null) { InstantiateBooster(); } else { InitBooster(booster); } } void InstantiateBooster() { Booster booster = GameObject.Instantiate<Booster>(boosterTemplate, transform, true); InitBooster(booster); boosterPool.Add(booster); } Booster FetchFromPool() { Booster booster = null; for (int i = 0; i < boosterPool.Count; i++) { booster = boosterPool[i]; if (!booster.IsEnabled()) { return booster; } } return null; } void InitBooster(Booster booster) { booster.Play(); booster.transform.position = boosterPosition; } } using UnityEngine; public class Booster : MonoBehaviour { [SerializeField] SpriteRenderer spriteRenderer; [SerializeField] Animator animator; public void Play() { animator.SetTrigger("enable"); } public bool IsEnabled() { return spriteRenderer.enabled; } } using UnityEngine; +using Treader; public class PlayerJump : MonoBehaviour { //PlayerJump.cs + BoosterPool boosterPool; + private void Start() { playerData.playerCollisionEvents.OnTopOfSomething += HandleOnTopOfSomething; playerData.playerCollisionEvents.OnObstacleCollisionExit += HandleObstacleCollisionExit; + + boosterPool = ServiceLocator.Instance.GetService<BoosterPool>(false); } //PlayerJump.cs void InitJump() { jumpVector = transform.position; currentForce = playerData.playerConfig.jumpForce; if (!doubleJump) { canJump = false; + boosterPool.TriggerBoost(transform.position); }     Upcoming End screen animation Config reference refactoring Hotkeys for ease of screen change Prevent the player from passing through obstacles

Treader

Treader

 

Dev Log #5

Intro I have had to face a bit of a crunch time at work causing me to fall back on my sprint so in favor of keeping up with that I have had to delay this blog. That said this sprint ended up being a bit more tricky than I had anticipated mainly having to do with not using unity physics added a bit of complications. However the main focus of this sprint has been the ability for the obstacle to push the player back and the end screen. Which I am happy to say is done. Input So I have finally gotten my input setup which is as follows. X : Action WASD: Movement Esc: To go back to the main screen S: To submit score (To be implemented) Score Keeper Now since I need to who the players score when the game ends I needed something to keep track of that score. So now I have a score keeper class whose responsibility is just that to keep track of the score. To be fair I did not anticipate this so the realization that I needed this ended up causing a sort of crunch of its own as I needed to finish this before I could even start implementing the end screen which is what I was originally planning on doing. During the course of working of this class I also came to realize the need  for a way to manage the reference for this however I have decided that, that is a problem for another sprint. In my previous game I solved this problem using something called a Service locator however I am hoping this time I can find something better. Dependency injection seems to be a concept I may experiment with this on. using UnityEngine; //TODO: Need a better way to handle ScoreKeeper refrence public class ScoreKeeper : MonoBehaviour { int score = 0; float currentTime = 0; bool canUpdateScore = false; public int pScore { get { return score; } } public void BeginScoreKeeping() { ResetScore(); canUpdateScore = true; } public void StopScoreKeeping() { canUpdateScore = false; currentTime = 0; } void ResetScore() { score = 0; currentTime = 0; canUpdateScore = false; } void Update() { if (canUpdateScore) { UpdateScore(); } } void UpdateScore() { currentTime += Time.deltaTime; if(currentTime >= 1) { score++; currentTime = 0; } } }   Gameplay State (Changes only) So I had previously already setup the game play state so I wont be showing the entire thing here again only the changes. So far when my state changes to gameplay I enable the character and Initialize the scorekeeper. At the same time when the gameplay state ends I stop the score keeping but do not reset the scorekeeper as it required for the end state. [SerializeField] GameObject playerCharacter; [SerializeField] ScoreKeeper scoreKeeper; public override void Init() { base.Init(); playerCharacter.SetActive(true); scoreKeeper.BeginScoreKeeping(); } public override void DeInit() { base.DeInit(); playerCharacter.SetActive(false); scoreKeeper.StopScoreKeeping(); }   End State Before we can have the end screen UI we need to have the end state setup currently the only job of the end state is to Initialize and DeInitialize the EndScreen UI. using UnityEngine; using Treader; public class EndScreenLogic : StateBase { [SerializeField] UiEndScreen endScreen; public override void Init() { base.Init(); endScreen.Init(); } public override void DeInit() { base.DeInit(); endScreen.DeInit(); } }   PlayerCollisionEvents So before the end screen can be shown the player needs to be able to be pushed back by the obstacle. The end screen as per the design is supposed to trigger when the player reaches the end and is in contact with the obstacle essentially being crushed between the end and the obstacle. To check this I need to know whether or not the player is at the end for this I have added a new callback in PlayerCollisionEvents which I am triggering from update. Now I dont want a registering class to receive this call back at every frame so I keep track of a bool so that only when the callback has something new to report only then the callback will trigger. Aside from that there are also other events that need to be kept track of this is done through the PlayerCollisionEvents. using System; using UnityEngine; using Treader; //TODO: For IsOnTop handle within x bounds as well //TODO: Need a better way to handle PlayerCollisionEvents refrence public class PlayerCollisionEvents : MonoBehaviour { public Action OnTopOfSomething; public Action OnCollideObstacleLeft; public Action OnObstacleCollisionExit; public Action<bool> OnPlayerAtEnd; [SerializeField] LayerMask mixedSafeMask; [SerializeField] LayerMask safeMask; [SerializeField] Transform endMarker; Collider2D eventsCollider; bool isAtEnd = false; bool prevIsAtEnd = true; //At the start this bool needs to be diffrent than isAtStart void OnTriggerEnter2D(Collider2D collision) { eventsCollider = collision; if (IsOnTop()) { if (OnTopOfSomething != null) { OnTopOfSomething(); } } if (IsObstacleLeft()) { if (OnCollideObstacleLeft != null) { OnCollideObstacleLeft(); } if (isAtEnd && !IsOnTop()) //This will happen when the player dies { TreaderFSM.Instance.ChangeState(States.END_SCREEN_STATE); } } } void OnTriggerExit2D(Collider2D collision) { if (((1 << eventsCollider.gameObject.layer) & safeMask) != 0) { if (OnObstacleCollisionExit != null) { OnObstacleCollisionExit(); } } } bool IsOnTop() { if (((1 << eventsCollider.gameObject.layer) & mixedSafeMask) != 0) { if (VectorMath.GetDistanceAlongY(transform.position, eventsCollider.transform.position) > eventsCollider.bounds.extents.y) { return true; } } return false; } bool IsObstacleLeft() { if (((1 << eventsCollider.gameObject.layer) & safeMask) != 0) { if (VectorMath.GetDistanceAlongX(transform.position, eventsCollider.transform.position) > eventsCollider.bounds.extents.x) { return true; } } return false; } void DoAtEndEvent() { isAtEnd = endMarker != null && VectorMath.GetDistanceAlongX(transform.position, endMarker.position) < 1; if (prevIsAtEnd != isAtEnd && OnPlayerAtEnd != null) { OnPlayerAtEnd(isAtEnd); } } private void Update() { DoAtEndEvent(); } }   PlayerIdle As the world needs to look like its moving the character cannot simply stay in the same place in the idle state the character when not subject from any input by the player needed to move back as well this is handled through PlayerIdle.  using UnityEngine; public class PlayerIdle : MonoBehaviour { [SerializeField] Transform endMarker; [SerializeField] PlayerData playerData; float worldSpeed = 3; //TODO: this needs to come from config Vector3 idleMoveVector; bool isLeftOfObstacleColliding = false; bool isAtEnd = false; // Use this for initialization void Start () { playerData.playerCollisionEvents.OnCollideObstacleLeft += HandleCollisionObstacleLeft; playerData.playerCollisionEvents.OnObstacleCollisionExit += HandleObstacleCollisionExit; playerData.playerCollisionEvents.OnPlayerAtEnd += HandlePlayerAtEnd; idleMoveVector = transform.position; } // Update is called once per frame void Update () { if (IsIdle()) { DoIdle(); } } void HandleCollisionObstacleLeft() { isLeftOfObstacleColliding = true; } void HandleObstacleCollisionExit() { isLeftOfObstacleColliding = false; } void HandlePlayerAtEnd (bool atEnd) { isAtEnd = atEnd; } bool IsIdle() { isAtEnd = endMarker != null && VectorMath.GetDistanceAlongX(transform.position, endMarker.position) < 1; if ((Input.GetButton("Horizontal") && !isLeftOfObstacleColliding) || isAtEnd) { return false; } return true; } void DoIdle() { idleMoveVector = transform.position; idleMoveVector.x = idleMoveVector.x - (worldSpeed * Time.fixedDeltaTime); transform.position = idleMoveVector; } }   EndScreen UI And finally for what is the highlight of this sprint the one thing that ties the game in one neat loop the end screen. This screen will show you your score, it will allow you to return to main menu and restart the game should you wish to.   using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using Treader; public class UiEndScreen : MonoBehaviour { [SerializeField] Text scoreText; [SerializeField] ScoreKeeper scoreKeeper; // Use this for initialization public void Init () { scoreText.text = string.Format("You lasted {0} seconds", scoreKeeper.pScore); gameObject.SetActive(true); } public void DeInit() { gameObject.SetActive(false); } private void Update() { if (Input.GetButtonDown("Action")) { TreaderFSM.Instance.ChangeState(States.GAMEPLAY_STATE); } if (Input.GetKeyDown(KeyCode.Escape)) { TreaderFSM.Instance.ChangeState(States.MAIN_MENU_STATE); } } }   Upcoming Adding a world config Implementing a better way to handle references Code refactoring Character horizontal movement..

Treader

Treader

 

Dev Log #4

Intro This has been some time coming but I guess that is to be expected there wasent enough new content to merit an entire blog post till now since so far the focus has only been polishing whats already there and to be fair I have been losing a little motivation for writing blog posts at the same time I feel like having a record of my work like this is important and in all fairness is pretty cool too look back on so I will continue doing this. That said there has been a major update which is the jump animation. During the course of my integrating the jump animation I realized unity has something called Blend tree for its animation ....I had no idea something like this even existed so I ended up learning something new after all.   StartUp The way my framework well works is that it changes to the state the game currently needs to be in essentially prepares the state getting everything that needs to be ready for that state and removing the things not needed for that state so I needed some way to start the first State as soon as the mainscene loads so I ended up with the StartUp class. The responsibility of this class is to change the state to the required default state as soon as the scene loads or more broadly handle startup operations. public class StartUp : MonoBehaviour { [SerializeField] SceneLoader sceneLoader; // Use this for initialization void Start () { sceneLoader.OnSceneLoadDone += HandleSceneLoaded; } private void HandleSceneLoaded() { StartCoroutine(WaitAndSetState()); sceneLoader.OnSceneLoadDone -= HandleSceneLoaded; } IEnumerator WaitAndSetState() { yield return new WaitForSeconds(0.1f); TreaderFSM.Instance.ChangeState(States.MAIN_MENU_STATE); } }   Loading So initially I wanted to have a loading bar but after some debugging I find that the loading takes 1.6 seconds max and from a user perspective it takes even less. The problem I faced was how do I visually verify if my loading screen is loading correctly if the entire thing takes less time than I take to blink. So I decided to simplify my loading  screen significantly from my initial design atleast till a time where my main scene is large enough to take more time to load. And its not like I am downloading anything.  UILoading This component attached the loading UI is responsible for telling the scene loader to load the scene. public class UILoading : MonoBehaviour { [SerializeField] SceneLoader sceneLoader; // Use this for initialization void Start () { sceneLoader.LoadMainScene(); } }   Config Just as I did in my last game I decided to use scriptable objects to maintain my configurable data currently only for player and obstacles. Player Config Currently maintains jump force and  gravity modifier values. public class PlayerConfig : ScriptableObject { public float jumpForce = 20; public float gravityModifier = 0.5f; } Obstacle Config Currently maintain the initial speed and initial position. public class ObstacleConfig : ScriptableObject { public float horizontalSpeed = 6; public Vector3 initialPosition; }   Player Jump Finally to the main update for this current sprint the jump animation just like last time this is more show than tell kind of situation. Just to be clear I did not create the animations I only did the integration the animations are part of the package from the asset store.  void DoJump() { playerData.mainAnimator.SetBool(PlayerAnimatorKeys.GROUNDED, false); playerData.mainAnimator.SetFloat(PlayerAnimatorKeys.JUMPSPEED, currentForce); currentForce -= playerData.playerConfig.gravityModifier; jumpVector.y = (transform.position.y + (currentForce * Time.fixedDeltaTime)); transform.position = jumpVector; }   Upcoming Next on my list is: 1. Pushing the character when they encounter an obstacle. 2. Build and integrate the end screen UI to tie the whole thing in a neat bow even if incomplete 3. Finally finish my second sprint.

Treader

Treader

 

Dev Log #3

Intro Writing this one at an odd time middle of the week rather than on a Saturday like I had originally planned but better to do something late than never do it at all ...well not always I figure you could find exceptions but this is int one of those. By the time of writing this post I have finally finished the first sprint of the project and plan out the second sprint the second sprint seems to be just a little smaller than the first one but not by much initially I was conflicted on whether or not to dedicate the second sprint only for polishing since that seems to be the majority of what it is right now however given its the second sprint I feel its important to finish the end case of the game which is mostly UI. Main Character So when I started my search for art and animation assets for the character I was really hoping to find one with legs and I did find a few but those tended to be sprites with a weapon held in their arms or too medieval for the game I am making. Eventually I found one with had an urban look with rigged 2D animation that could work I dint really like the look a lot but it was the only one suitable which had legs. That said there was one I fell in love with that dint have legs ...a cool looking furball character I kept debating with myself for a while which went along the lines of 'This character dosent have any legs if I use it can I still call it a runner?' then I eventually figured if we can call an electronic appliance running just because its functioning and not even moving e.g "Is your fridge running?" a furball that's moving can definitely count as running. Player Data This class maintains references for not primitive data relevant to the player my plan is to have different classes for different actions so having a single class maintain references might reduce the overall number of references that need to be maintained.  public class PlayerData : MonoBehaviour { public Animator mainAnimator; public Animator cloudAnimator; public LayerMask groundLayerMask; }   Player Animator Keys This class just maintains a const for animator conditions. public class PlayerAnimatorKeys { public const string SPEED = "Speed"; }   Player Jump So far I have only added the jump functionality for the player. Initially I wanted to use rigidbody2D for the jump and other motion but even when I set ground and player bounce to 0 in the physics material I still noticed an unwanted bounce and the animation was also affecting the rigidbody velocity in an unwanted way so I decided to go ahead and make a custom jump script. public class PlayerJump : MonoBehaviour { [SerializeField] PlayerData playerData; [SerializeField] float jumpForce = 20; //TODO: Needs to come from config [SerializeField] float gravityModifier = 0.5f; //TODO: This needs to come from config bool canJump = true; Vector3 jumpVector; Vector3 fallVector; float ground = 0; float currentForce; private void Start() { ground = transform.position.y; } void InitJump() { jumpVector = transform.position; currentForce = jumpForce; canJump = false; } void DoJump() { currentForce -= gravityModifier; jumpVector.y = (transform.position.y + (currentForce * Time.fixedDeltaTime)); transform.position = jumpVector; } private void OnTriggerEnter2D(Collider2D collision) { if (((1 << collision.gameObject.layer) & playerData.groundLayerMask) != 0) { canJump = true; } } private void Update() { if (Input.GetButtonDown("Jump") && canJump) { InitJump(); } } private void FixedUpdate() { if (!canJump) { DoJump(); } } }   Obstacles Currently I have only a single type of obstacle that is nothing but a white square colored and stretched out into a rectangle. For the obstacle I have decided to go with object pooling since here will be a lot  of them and destroying and initializing seems excessive. Obstacle This is the class attached to all obstacles now that I think of it I might need to make this more specific since there will be a lot more kinds of obstacles in the future and the name might be too vague. Currently all it does is move the obstacle in a single direction. One thing I learned making this game is making the obstacles move in a infinite runner game to give an impressions of the world moving is much easier than making the world move. public class Obstacle : MonoBehaviour { public Action<Obstacle> OnReachedEnd; [SerializeField] float horizontalSpeed = 6; //TODO: This needs to come from config [SerializeField] Vector3 initialPosition; //TODO: this needs to come from congif [SerializeField] GameObject endPoint; Vector3 moveVector; private void Start() { moveVector = initialPosition; } void Move() { moveVector.x = moveVector.x - (horizontalSpeed * Time.fixedDeltaTime); transform.position = moveVector; } private void FixedUpdate() { Move(); } private void OnTriggerEnter2D(Collider2D collider) { if(collider.gameObject == endPoint) { moveVector = initialPosition; if (OnReachedEnd != null) { OnReachedEnd(this); } } } }   Obstacle Populator This is the class responsible for pooling the obstacles and initializing them from prefab. Current idea is as soon as the  first obstacle reaches the end we have enough obstacles and to stop creating more and begin pooling instead. public class ObstaclePopulator : MonoBehaviour { [SerializeField] Obstacle obstacleTemplate; [SerializeField] float timeInterval; //TODO: This should come from config List<Obstacle> obstaclePool; bool stopPopulaing = false; // Use this for initialization void Start () { obstaclePool = new List<Obstacle>(); StartCoroutine(PopulateAtInterval()); } void InitializeObstacle() { Obstacle obstacle; if (obstaclePool.Count == 0) { obstacle = GameObject.Instantiate<Obstacle>(obstacleTemplate, transform); } else { obstacle = obstaclePool[0]; } obstacle.gameObject.SetActive(true); obstacle.OnReachedEnd = HandleObstacleReachedEnd; if (obstaclePool.Contains(obstacle)) { obstaclePool.Remove(obstacle); } } void HandleObstacleReachedEnd(Obstacle obstacle) { obstaclePool.Add(obstacle); obstacle.gameObject.SetActive(false); obstacle.OnReachedEnd = null; } IEnumerator PopulateAtInterval() { while (true) { yield return new WaitForSeconds(timeInterval); InitializeObstacle(); } } }   Main Character and Obstacles  Not much to say here so I leave you with a video of how the gameplay looks so far.     Upcoming By the next log I hope to have some polishing done such as have scriptable objects to make the data configurable. Add a startup class to initialize my state. Add a boost particle effect during jumps.

Treader

Treader

 

Dev Log #2

Intro This has been a long time coming unfortunately the last two weeks have been too busy with long nights and missed weekends I have fallen behind on this project. The framework is however ready and very minimum UI functionality is done as well. Unlike the previous dev log this will be code heavy with no images. Editor So first things first I decided to make the main scene into a single asset bundle and then load it into the game at runtime This script is pretty self explanatory it just builds a bundle and puts it in the StreamingAssets folder. using UnityEditor; using System.IO; public class CreateAssetBundles { [MenuItem("Assets/Build AssetBundles")] static void BuildAllAssetBundles() { string assetBundleDirectory = "Assets/StreamingAssets"; if (!Directory.Exists(assetBundleDirectory)) { Directory.CreateDirectory(assetBundleDirectory); } BuildPipeline.BuildAssetBundles(assetBundleDirectory, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows); } }   Framework This is my framework lot of it has been brought in from my previous project. Async Bundle Loader The below script simply loads the asset bundle and only the asset bundle and not any asset that is inside the bundle so far I haven't needed that functionality so I haven't added it in. When the need arises I will add that in as well. This is really where I started experimenting with loading assets in co-routines to avoid mini freeze. using System.Collections; using UnityEngine; using System; using System.IO; public class AsyncBundleLoader : MonoBehaviour { Action<bool, UnityEngine.Object> OnResourceLoaded; public static AsyncBundleLoader instance; string path; private void Start() { instance = this; } //Only loads the assetbundle not an asset inside the bundle public void LoadBundle(string path, Action<bool, UnityEngine.Object> callback) { OnResourceLoaded = callback; this.path = path; StartCoroutine(StartAsyncLoad()); } IEnumerator StartAsyncLoad() { var bundleLoadRequest = AssetBundle.LoadFromFileAsync(Path.Combine(Application.streamingAssetsPath, path)); yield return bundleLoadRequest; AssetBundle myLoadedAssetBundle = bundleLoadRequest.assetBundle; if (myLoadedAssetBundle == null) { Debug.LogError("Failed to load AssetBundle at path: "+path); OnResourceLoaded(false, null); yield break; } OnResourceLoaded(true, myLoadedAssetBundle); myLoadedAssetBundle.Unload(false); } } Scene Loader This class was mainly for me to load my scene asynchronously after the scene bundle is loaded so the loading begins in the startup scene then once the scene is loaded we move into it this prevents any chance of the game getting suck while its loading. using System.Collections; using UnityEngine; using UnityEngine.SceneManagement; public class SceneLoader : MonoBehaviour { const string path = "mainscene"; AssetBundle mainSceneBundle = null; void Update() { // Press the space key to start coroutine if (Input.GetKeyDown(KeyCode.Space)) { // Use a coroutine to load the Scene in the background AsyncBundleLoader.instance.LoadBundle(path, HandleMainSceneLoaded); } } private void HandleMainSceneLoaded(bool isLoaded, UnityEngine.Object inObj) { if (isLoaded) { mainSceneBundle = inObj as AssetBundle; StartCoroutine(LoadYourAsyncScene()); } else { Debug.LogError("Failed to load the main scene"); } } IEnumerator LoadYourAsyncScene() { string path = mainSceneBundle.GetAllScenePaths()[0]; AsyncOperation asyncLoad = SceneManager.LoadSceneAsync(path); // Wait until the asynchronous scene fully loads while (!asyncLoad.isDone) { yield return null; } } }   State Base This is the parent class to every State logic class this mainly defines the signature of the state and dosent contain any implementation of its own I considered using interface for a signature but the implementation I had in mind required references to be added from inspector and you cant do that without Monobehaviour. using System.Collections; using System.Collections.Generic; using UnityEngine; namespace Treader { public abstract class StateBase : MonoBehaviour { public string stateName; // Use this for initialization public virtual void Init() { } public virtual void DeInit() { } } } States This is a convenience class  to maintain and access the state names. using System.Collections; using System.Collections.Generic; using UnityEngine; public class States { public const string MAIN_MENU_STATE = "MainMenu"; public const string GAMEPLAY_STATE = "Gameplay"; } FSM This is the main state class responsible for handling  the state change. using System.Collections.Generic; using UnityEngine; namespace Treader { public class TreaderFSM : MonoBehaviour { [SerializeField] List<StateBase> stateBases; [SerializeField] string defaultState; private static TreaderFSM instance; private StateBase currentState; public static TreaderFSM Instance { get { return instance; } private set { instance = value; } } public void Awake() { instance = this; if (!string.IsNullOrEmpty(defaultState)) { ChangeState(defaultState); } } public void ChangeState(string stateName) { for (int i = 0; i < stateBases.Count; i++) { if (stateBases[i].stateName.Equals(stateName)) { if (currentState != null) { currentState.DeInit(); } currentState = stateBases[i]; currentState.Init(); } } } } } Gameplay Currently there isint much here so far I only have the state logic class setup. Which at this point is nothing but an empty class that inherits from StateBase. UI Buttons So I am not going to be using the unity's button script mainly since I dont want button click functionality for this game so I will be writing my own button classes. IButton Each button class will inherit from this interface the purpose is so that I can use the OnButtonSelect function of the button without needing a reference to the specific button class. namespace Treader.UI { public interface IButton { void OnButtonSelect(); } } Start Button Currently the start button only changes the state and in all fairness that should really be all it should need to do. using UnityEngine; using Treader.UI; using Treader; public class StartButton : MonoBehaviour, IButton { public void OnButtonSelect() { TreaderFSM.Instance.ChangeState(States.GAMEPLAY_STATE); } }   Main Menu While the main menu has been visually set up its a good deal away from being done for now the State logic and UI class for the main menu have been setup.   Main Menu Logic This is the class that is responsible for handling all the defining what happens when a state changes to and from the MainMenu. Currently its just enables and disable the ui/ using Treader; using UnityEngine; public class MainMenuLogic : StateBase { [SerializeField] UiMainMenu mainMenu; public override void Init() { base.Init(); mainMenu.gameObject.SetActive(true); } public override void DeInit() { base.DeInit(); mainMenu.gameObject.SetActive(false); } }   UI Main Menu This class is responsible for the visuals of the main menu currently it handles registering the buttons and highlighting the selected button. using System.Collections.Generic; using Treader.UI; using UnityEngine; using Treader; public class UiMainMenu : MonoBehaviour { [SerializeField] GameObject[] buttonsGameObjects; [SerializeField] Transform selector; List<IButton> buttons; int selectedIndex = 0; // Use this for initialization void Start () { buttons = new List<IButton>(); RegisterButtons(); UpdateSelector(0); } void RegisterButtons() { buttons.Clear(); if (buttonsGameObjects != null) { for (int i = 0; i < buttonsGameObjects.Length; i++) { IButton button; button = buttonsGameObjects[i].GetComponent<IButton>(); Debug.Assert(button != null, "Failed to add buttons please makes sure all buttons inherit from IButton"); if (button != null) { buttons.Add(button); } } } } void UpdateSelector(int index) { if (index < buttonsGameObjects.Length) { selector.position = buttonsGameObjects[index].transform.position; selectedIndex = index; } } private void Update() { if (Input.GetKeyDown(KeyCode.X)) { buttons[selectedIndex].OnButtonSelect(); } } }   Utilities Last but not least the utility classes these are classes that help with very specific things that are common such as following a transform or keeping an object persistent. While there are other utility classes than the one I am mentioning here since they are not being used currently I am leaving them out for the time being.   Persist Object This class simply marks the object its attached to persistent which keeps the object between scenes. using UnityEngine; //Simple helper component class to keep the game object in scene public class PersistObject : MonoBehaviour { private void Awake() { DontDestroyOnLoad(this.gameObject); } }   Upcoming The next devlog will be when my first sprint is done I should have the character control done as well as some basic obstacles functional so the next dev log should have a mix of code and images.

Treader

Treader

 

Dev Log #1

Intro Its been little over a week since I started this project. So far I have setup the Bitbucket and sprint on Trello and Ollert so my planning and version control seems solid. Aside from that I have finished some other things I will list down. So far it seems I am ahead of track despite my bumpy progress, currently no lines of code have been written but that is high on the todo. Splash Screen This was really tricky not being an artist myself I had go to search for ready made templates I could use all sites I found usually charge a registration fee which for the scope of the current project seemed too high maybe some other time. So I did the next best thing loaded up Gimp watched a few youtube tutorials got some fancy text going added a bit of highlight and I have a splash screen logo ....is it as good as the templates found on other sites not by a long shot but it will do. Loading Screen To do this I had to first get a UI kit from the unity asset store I found I nice old school themed one which I think works well for that I have planned. The loading screen main purpose is really just to wait for the main scene to load initially I was planning on having it load individual assets but in hind sight those will load too fast for the loading screen to be of any use given the scope of the game but who knows the future may prove me wrong and I might still do this. So far the loading screen will mostly be a fake one going from 1-90 then stopping to wait till everything is loaded then moving to a 100% that is if everything hasent already loaded in which case it will fill directly to 100. Main Menu Initially I was planning on implementing the main menu but if I want to have a transparent main menu with game running in the background but without the player character present. So I have pushed the main menu implementation to the last since I need to get the basic game play done first. The UI however is done and setup the buttons will not be mouse clickable and instead will use arrows keys to select them and action to trigger.   Upcoming For the next dev log I should have the FSM and loading and building asset bundles ready that should be more code heavy while this one was image heavy.

Treader

Treader

 

Dev Log #0

Description So its been a few days since I finished my last project time to pick up a new one I have decided to make an infinite runner game similar to the no connection dinosaur game in your chrome web browser. The idea is to have a good music with tense game-play kinda like flappy bird but maybe not that hard and relaxing background. This is the first dev log made before the project starts hence nothing to show currently no images and such I will be adding those in future updates once I get the project started.    Feature List Game Play Basic 2D character movements left, right and up (jump) Double Jump  Power Jump Moving pillars that can push you back and also move up and down Randomly placed spikes that can kill you if you touch them Basic 2D run and jump animation Projectiles that move from left to right and can kill you UI Splash Screen Loading Screen Main Menu Instructions screen High score Screen About Us (Maybe if I think I have something to put in here ..maybe a link to the previous project) Options menu with sound and music toggle End screen In Game HUD   Back End Get and Set high score   Now that I am actually typing all this out it does seems like a lot but I have found that doing things one sprint at a time helps maintain motivation and its pretty cool to see a nice progress graph. I will either do these every week or every sprint havent decided that yet I figure I will have a better idea of how often to do these once I get started.

Treader

Treader

  • 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!