Thursday, December 28, 2017

Particularly Wavy Update

hey all,

I have not gotten nearly as much work done as I wanted to this week. I've had more or less full-time work hours at my (kind of) paying job, which has greatly reduced the hours I can put into the game.

But what I have got done is this: I now have 41 puzzles complete and playable. The first 30 deal with reflection, while the next 20 (once completed) will deal with refraction. In that vein, I have removed a vicious set of bugs due to some recursive functions that I was using to deal with interior reflection inside a prism. These bugs at first caused Unity's garbage collector system to go haywire, with lots of errors spamming the console about "78 bytes allocated at " some random location, and after getting those to stop, I ran into a bug that caused Unity to simply crash. So, I was forced to greatly simplify the calculation of these internal reflections, since they could cause the light to bounce around inside the prism 5, 10, 20, or even 30 times before exiting, and Unity would have to deal with potentially 100's of game objects being created and destroyed each frame if the player was moving or rotating the prism.

I have also submitted the game to BitSummit 2018, which due to my excitement about going to GDC earlier this year I forgot to do for BitSummit 2017. I'll be updating everything I can about the game in terms of publicity before we enter 2018: the latest builds for 32-bit and 64-bit will be uploaded to BitSummit and to the game's page on itch.io, there will be a devblog on itch.io about the updates, and Twitter shall crash due to all the views and retweets about my amazing game, and as Wayne Campbell would say, "Cha, and monkeys might fly out of my butt!"




Well, in any case, I have actually updated my BitSummit submission, the itch.io version, posted the game to FaceBook and to Twitter, and YouTube. And I have spent more time than I care to admit tracking down database errors, problems loading and saving XML during runtime after compiling and building the game, making sure that Unity is properly saving the data about each object that I place and configure while I am designing the levels (which since I am using a number of custom inspector scripts, is actually quite difficult or at least annoying), and a host of other obstacles.

That is all the work I can put in on the game in 2017 and maintain my sanity.My wife and I will be out of Japan on a much deserved vacation until January 8th, 2018, so I will not be programming or designing anything. Although, like any other time, I will probably come up with a few ideas for prototypes, new games, and things while I am brushing my teeth, taking a shower, or staring into space or enjoying a view on our trip.

Peace and love to all my friends and family. If you had a great year in 2017, I hope 2018 knocks your socks off, and if 2017 put you through the wringer, I hope you put 2018 through it instead.

Thursday, December 21, 2017

Latest Update For Particularly Wavy

hey all,

It's been a while since my last post here. Part of the delay has been due to me catching a cold, and the rest is just that I've been too busy coding to actually write or talk about what I've been coding. One thing that is shown in the video is really smooth detection of hits: in the very first version of the game, I was performing raycasts every frame and based on those results, deciding if I needed to recalculate the lights, which as you might imagine leads to lots of frames where I perform the raycast and decide to do nothing.

In the latest version, I have delegates and events on every object that moves, rotates, or changes size, and when they do one of those things, the laser receives a message letting it know that something has moved, so it can then update the light positions and angles.



The problem was that when the light hit something, I was starting certain coroutines that would spam a message to the hit object once every frame, and in the code for the hit object I was running a bunch of checks inside the Update() function, which is run once every frame. Why would I do something stupid like that? Well, say you perform your calculation and you determine that light ray A is now hitting object O. You set the hit object variable of light ray A to object O and go on calculating what other results fall out from that. But what if on the previous frame light ray A was hitting object T, and object T is the target? In my game, I have a flag set inside the Target.cs script so that it knows when it is hit and what it is hit by. How do I tell the Target that it is no longer hit? That is the reason for the coroutines and Update() code: if the Target stopped receiving the message of being hit from the coroutine, which is stopped whenever I update the light anyway, then the Target can act correctly.

If you look at the code below, however, you will notice a different technique. I have a private variable called hitObject, and I have a public accessor for this called HitObject. Inside the setter, I run comparisons between the incoming value and the previous value of hitObject. Based on those, I send messages using Unity's built-in messaging system. These messages let the object know that it is no longer hit by or has just been hit by a particular light ray, as the case may be. Problem solved: no messy coroutines that need to be started or stopped, no code running every frame inside of Update() (OK, OK, no code besides the shader material updates) and using up CPU time. When something changes, the messages are sent and if nothing has changed, then nothing needs to be updated or checked.


using System.Collections.Generic;
using UnityEngine;

//[RequireComponent(typeof(CapsuleCollider))]
public class RayNode : MonoBehaviour
{
 private GameObject hitObject;
 public List < RayNode > children;
 public LineRenderer rayLR;
 public int depth;
 public bool influencedByBlackHole;
 public bool intensified;
 public GameObject metaball;

 public GameObject HitObject
 {
  get { return hitObject; }
  set
  {
   if ((value == null && hitObject != null ))
   {
    hitObject.SendMessage("OnLightExit", gameObject, 
SendMessageOptions.DontRequireReceiver);
    hitObject = null;
   }
   else if ((value != null && hitObject != value && hitObject != null ))
   {
    hitObject.SendMessage("OnLightExit", gameObject, 
SendMessageOptions.DontRequireReceiver);
    hitObject = value;
    hitObject.SendMessage("OnLightEnter", gameObject, 
SendMessageOptions.DontRequireReceiver);
   }
   else if ((hitObject == null && value != null))
   {
    hitObject = value;
    hitObject.SendMessage("OnLightEnter", gameObject,
SendMessageOptions.DontRequireReceiver);
   }
   else
   {
    hitObject = value;
   }
  }
 }

 float offset;

 private void Start()
 {
 }

 private void Update()
 {
  offset -= Time.deltaTime * 2f;
  if (offset < -.5f)
  {
   offset += .5f;
  }
  rayLR.materials[1].SetTextureOffset("_MainTex", 
new Vector2(offset / 4f, 0));
  rayLR.materials[2].SetTextureOffset("_MainTex", 
new Vector2(offset, 0));

 }


 public RayNode()
 {
  children = new List < RayNode > ();
 }

 public RayNode(GameObject RO)
 {
  hitObject = RO;
  children = new List < RayNode > ();
 }

 public void PruneSubTree(int childIndex)
 {
  //validate index
  if (childIndex > -1 && childIndex < children.Count)
  {
   RayNode toPrune = children[childIndex];
   //set the hitObject to null
   if (toPrune != null)
   {
    toPrune.HitObject = null;
   }
   

   List < RayNode > ch = new List < RayNode > ();
   for (int i = 0; i < toPrune.children.Count; i++)
   {
    ch.Add(toPrune.children[i]);
   }

   //reverse order is important in order to prevent skipping errors
   for (int i = ch.Count - 1; i > -1; i--)
   {
    toPrune.PruneSubTree(i);
   }

   if (toPrune != null)
   {
    Destroy(toPrune.gameObject, 0.1f);
   }
   
   children.Remove(toPrune);
  }
 }

 public List GetChildren()
 {
  return children;
 }

 public void PruneWholeTree()
 {
  if (children.Count > 0)
  {
   List < RayNode > ch = new List < RayNode > ();
   for (int i = 0; i < children.Count; i++)
   {
    ch.Add(children[i]);
   }

   //reverse order is important in order to prevent skipping errors
   for (int i = ch.Count-1; i > -1; i--)
   {
    PruneSubTree(i);
   }
  }
 }

 public void UpdateChildren(Vector3 startPosition)
 {
  for (int i = 0; i < children.Count; i++)
  {
   children[i].gameObject.transform.position = startPosition;
   children[i].rayLR.SetPosition(0, startPosition);
  }
 }

 public void HandleMetaBall()
 {
  metaball.transform.position = (rayLR.GetPosition(0) + rayLR.GetPosition(1)) / 2f;
  Vector3 dir = rayLR.GetPosition(1) - rayLR.GetPosition(0);
  metaball.transform.rotation = Quaternion.AngleAxis(
Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg,
 Vector3.forward);
  float scaleFactor = 0;
  
  //set scaling factor in case of low distances
  if (Vector3.Distance(rayLR.GetPosition(1), rayLR.GetPosition(0)) < 5f)
  {
   scaleFactor = 0.2f;
  }

  metaball.transform.localScale = new Vector3((scaleFactor + 1.1f) * 
Vector3.Distance(rayLR.GetPosition(1), rayLR.GetPosition(0)), 2f, 1f);
 }
}


This simple fix solved several other problems as well. The targets need to keep track of what light is hitting them, and previously each light ray would send a message letting the target know this. I would have to clear the list at the beginning of a light update cycle, then add each light ray, and at the end of a frame, I would have to perform a check to see if the target's condition had been met. The exact timing of that check is important, because performing the check in-between light rays being added would lead to false positives: the target needs to be hit by red light and only red light, for example, and after one check it is being hit by red light, so the condition is marked as being satisfied. But then orange light and yellow light send their messages to the target and now the condition is not satisfied. But the message has already been sent to the game manager, which now congratulates the player on solving the puzzle even though they have not solved the puzzle. You get the idea. Since I was performing these checks every time an object was moved, that just increased the chance that one poorly timed message would screw the whole thing up. Now, I only perform these checks when a light ray first hits the target and when it stops hitting the target, greatly reducing the chance of a false positive.

One final problem that has only come to light recently is null references. Normally, these would be huge signal fires that something has broken somewhere, but these only started showing up when I changed some of my laser code to be updated just as the program stops or shuts down. When I did that, suddenly the console was getting clogged by null reference errors. Luckily, the fix was really simple: inside the PruneSubTree function, I now check if the child to delete is null and if it is not, then I delete it. That's it: no more errors.

I've been spending so much time going through and fixing these problems, updating the chargeable objects and activate-able objects, etc, that I still have not gotten around to finishing the code for my heat-able objects. But I hope to have that finished before New Years.

Thursday, November 30, 2017

Final Feature, Revamping Old Ones

Hey all,

This week on Particularly Wavy I have been working on the final feature I intend to add to the game, in addition to revisiting some previously implemented ones and giving them a bit more depth and UI dazzle, if such a word can be used.

The final feature I'd like to add is the emission of light from heated objects. I have a script and shader that are working to update the appearance of an object which is hit by an intensified beam of light, and the next step is simply to add the actual emission of light from that object. This is more or less the easier part, since it follows almost the same rules as the lasers and objects which emit light due to collisions.

The features I'm revisiting are chargeable objects and movable objects. Chargeable objects originally were implemented to connect to other objects using a line renderer to display an arc between the two objects, but I never got this working to my satisfaction, so I've gone back and changed it to a particle system which travels between the two. I've also been adding more functionality to the objects connected to the chargeable objects. They can be of different types, like doors that swing open, lasers that get activated, etc. For movable objects, I'm trying to add tracks to show where and how they can move.

Saturday, November 25, 2017

Fog of War

hey all,

So I have been working on adding a fog of war effect to my game. Fog of War normally means an effect in strategy games where parts of the map without any player units are unlit and in order to reveal that area, the player will have to send a unit there. Doing so will light up that area of the map and reveal any enemy units that were hiding out there.

I wanted a similar effect, but instead of units, I wanted my beams of light to reveal parts of the level. It turns out that this was very wrong of me, as it took over two weeks and more than 63 hours of coding, banging my head against the monitor, research, more coding, more head-smashing, more research, etc.

At first, I thought I would just implement a simple fog of war effect using shaders and quads. This worked fine for single objects that did not extend to far in any one direction, but just didn't look very nice for light beams.

Then, I thought just using Unity's built in lights would be the next obvious step, but they did not produce the results I wanted, and had awful side effects like washing out the little color that I have in the game and making some elements look even more unattractive than before.

My penultimate step was to look at particle systems. This actually seemed like it might work, but I ran into bug after bug after bug, even after literally copying and pasting code from Unity's own website. So I gave up on that.

I was about to just quit and when I remembered a subject that I had researched in some detail months ago for my Digestion Game: metaballs! Now, before you get sick images of balls made of other balls, metaballs are simple a way to take overlapping sprites and combining their properties in various ways. They have been used for water rendering, influence mapping, and for lots of other stuff. I quickly found a few shaders that looked promising, tweaked their code, and set up some prefabs in Unity. The actual setup to produce the effect took less than an hour, but I spent a good 4 hours working out the best (to my mind and to my current abilities) solution for handling them together with my lights. It is still not fool proof, but I think I have got a pretty nice solution.




Wednesday, November 8, 2017

All Work and No Play...

hey all,

Yes, I'm still hard at work on Particularly Wavy. In the last week I have designed six new puzzles, many of which utilize the mechanics I've been describing in the last several blog posts. I hope to publish the game very soon, but I will continue to work on adding more puzzles over the next few weeks.

In addition to adding more content to the game, I've now implemented rudimentary scoring mechanics and, yes, have found and fixed even more bugs with my refraction calculations. Above a particular angle between the surface normal and the light vector (if you understand that phrase, then you already know what is coming next), the light will be reflected inside the prism instead of exiting it. This requires a different kind of calculation, and basically just re-complicated my refraction code that I had spent so many days simplifying. In any case, it seems to be working nicely, and without hogging too many system resources.

But don't get the idea that I am chained in front of my computer all day every day, slaving away at this game. I'm still enjoying playing video games that others have slaved over, such Total War: Warhammer, The Banner Saga, and Duskers. I'm pulling up to the 200 hour mark for Total War: Warhammer, and there are still two factions (Beastmen and Brettonia, plus another DLC I don't have yet for Norsca) that I haven't played, as well as two special campaign maps that I haven't done yet. And then there are still several games that I purchased months or years ago that I haven't gotten around to playing, like SpellForce 2, King of Dragon Pass, Democracy, and Crusader Kings II. And my wife and I are still planning trips to take in the near future, so we will be getting out and about and exploring this planet of ours.

Cheers,

Sunday, October 29, 2017

Ground-Up Rewrite

hey all,

I have been working to integrate lenses and destructible objects into the framework I've already developed, but this weekend I ran into a snag. As I was experimenting with moving and rotating my lens, I realized that the calculation for how light refracts through it was fundamentally flawed. Now matter how I rotated the lens, light was always bent down. The kicker to this was that I had basically copied the code I used for calculating refraction through a prism, which meant that those calculations were fundamentally flawed too. So over the whole weekend I have rewritten the refraction algorithms and calculations, greatly simplifying them and correcting the errors from before. I've also been correcting some small positional errors throughout the whole code base.

With these fixes and changes in place, the next week's work will focus on redesigning the first 18 levels, and on expanding the game with more puzzles to solve.

Cheers,

Sunday, October 22, 2017

Particularly Wavy Black Hole Update

hey all,
I've continued working on the black hole effect, and now, after several weeks of work I have something that I like quite a bit. This effect is, in my opinion, almost ready to be included in the game. The main catch is that right now I am not checking for any collisions within the influence of the black hole itself. There is now really nice way of doing this, but I think I have an idea that might work.



The other update to the game is actually not visible at all, but should greatly increase performance. In previous versions, I would update the light every frame, even if nothing had changed. In the newest version, I have made use of a callback function on every moveable object. When the object is moved or rotated, the function notifies the light emitting object and the light is recalculated from the beginning. If no object gets moved, the light is not updated, which should have a lot of calculations each frame and make the game run much smoother.

There are one or two main updates I would like to make to the game before starting work on designing more levels, but these should be doable within the next two weeks (as the famous construction joke goes).


I'd also like to make the UI compatible with mobile devices, but that is a distant 3rd on my list of things to work on. In any case, here goes to successful game development.