Throughout my career, I have found myself repeating the following mantra:
Don't build an engine in an engine. Use the engine.
If you need a way to have a flexible schema in SQL, implement SQL CREATE/INSERT COLUMN etc. If you need a series of page that follow one after another then use ASP.NET with HTML hyperlinks, don't build another engine inside it.
The building grid was going fine. You could select buildings and place them on the Cloudship base without physics going mental. It was a bit tricky to click on spaces in the middle but better camera movement would have helped with that. The next task was "rotate building". That's easy if you consider that the building is 1x1 but then it starts getting nasty if it's 2x2. I began working out the maths and realised that I was building a rotational engine. Which is mental. What if, I thought, what if I just removed the bloody grid?
Going gridless
All the things I was getting from the grid weren't that helpful:
- Collision was easy (was the space taken).
- Selecting a space to put the building was easy.
- Lining stuff up was easy.
However, some stuff started to get hard:
- Rotating buildings
- Buildings that were odd shapes (imagine Tetris piece from above).
- Filling up all of the base so that it looked crowded rather than having these edges
So I decided to clear out the grid and just plonk the buildings down. Here's a WIP montage:
I started naively, knowing full well that some things would need to change. I accidentally left physics on at one point and had good fun making the ship go mad. There's a possibility in the future for the physics engine to take the weight of the buildings into account.
As the video shows, I managed to get collision working but the buildings would overlap the edge. So began "edge detection" Odyssey.
Edge detection
The edge detection problem is:
The building must not overhang the edge (much)
My first thought was to have a series of flat planes will colliders on them. If a building touched these flat planes then it would disappear (can't be placed). Unity doesn't do collision on a flat plane. So many boxes, then? Possibly but costly in terms of time to set up on each base. Then I got the idea of rather than detecting the building entering a boundary, you detect it leaving one. I wanted to use a standard (optimised) collider rather than the expensive mesh collider but it's only for building purposes and you can't make the standard ones into an ellipse.
Here's my notes for edge detection:
The elliptical boundary collider would scale depending on how big the building is. Small buildings can get nearer to the edge, so the boundary collider would shrink only a little bit. When the building's collider stopped intersecting with the elliptical boundary collider then the event would fire, I could trap that and switch off the building.
That worked at first but as I was experimenting with bigger buildings, I realised that if the building took up more than half the width of the cloudship then the boundary collider would have a negative width and it would stop working. Instead I had to add more colliders to the buildings, split out the collision scripts and track "building colliding with building" and "building staying in the safe zone with boundary collider". So, rather than the boundary collider working with the actual dimensions of the building, it would only collide with a little collider.
Here's a little video if it in action:
As I move the first building toward the edge, it only disappears when the little collider leaves the safe zone of the elliptical boundary collider. The main building dimensions are still well within the boundary collider here.
I hope all that makes sense.
Next up
I'm going to play with the shader graph so that the building becomes an outline rather than disappearing completely. As part of all this work, I've updated Unity to 2018.1 and moved over to using the lightweight render pipeline. There's still a little bit of faff (you'll notice the lighting on the controller is gone overexposed) but I'll figure that out.
Rotating buildings is then REALLY easy and I can make another building or two.
Comments
Oh another little thing I've discovered is ShareX for taking little videos for portions of the screen and uploading them. Really handy.
OH MY GOD. SHADER GRAPH. OH. MY. GOD.
I made the shader I was after in 20 mins - most of which was me fiddling. There is a happy-problem that I need to watch and that's "buggering about tax". The effect of spending a little too much time on trying to achieve a perfect look, rather than making progress.
Selecting Building
I'm going to rubber duck program in text...
All this week I've been playing with "select and move building". It's actually a more complex proposition than you'd think. This is because of the difference between screen space (the flat array of pixels you look at) and local space (the 3 dimensions the objects are in). Here's an image, description below.
In this picture, you see the Cloudship with a boiler and I've just click-selected the back of the chimney about 2/3 of the way up. This is a debug cam picture, not the main game screen and so we're looking at local space, not screen space.
The green line is a debug ray that is cast from the camera (out of shot off the top right corner of the picture) to the point where I click the mouse. I have the Ray continuing its journey through the object but only to demonstrate a point below. If you could imagine the mouse pointer in this 3D local space it would be at the point the green ray hits the chimney.
When moving the building, I need to set the position of the building depending on the mouse position. When you set the position of the building, you're setting the location of the origin, which for the buildings is right at the bottom in the middle. The bottom of the blue line (I'll come back to) where it hits the red line is the position that I need to set.
I'm doing another check too. I need to make sure that the building is being put "onto" the Cloudship. If the player moved the mouse away and clicks anywhere else then that should not place a building.
When casting the green ray, I need to ignore other buildings. This is so that you can put a building behind another. Therefore, the green ray ignores buildings and that's why it's passing all the way through. If I were to try and set the position of the building to the green ray, you can see that it never hits the Cloudship, so would not be allowed. If the green ray did intersect the Cloudship, then it would jump forwards as the mouse point is not pointing at the base (the position of the building) but halfway up the chimney.
I need to maintain the offset of the mouse pointer and put the building at the green ray minus an offset to give me the base of the building and then cast a ray at that point. The red ray is really what I'm after.
Except I've not quite got it working!
It should be: red = green - blue.
However you see that the blue and green lines don't quite meet. The red ray is actually a bit of a con for proving my problem, it's a ray between the origin of the mouse and the building position; not the subtraction of green and blue. I must be doing something a little but odd with the vector maths (parallax aside, the blue line is clearly too long) and I will figure it out but this is where I had to leave it this morning when my time ran out.
I've also implemented a simple shader but I'll come back to that when I have selection working.
Ah the wonderfully confusing and somewhat complex art of mouse pointer ray casting into object space of an arbitrary plane with an offset. I've done this a little bit with opengl where it doesn't help you at all and things like this
http://antongerdelan.net/opengl/raycasting.html
are the sorts of matrix math you have to do.
Lol, actually, that who article is two lines in unity! Thank you, though, twas an interesting read.
Friday morning and this evening have been a struggle but I finally figured out what I was doing wrong. I was combining a unit vector (between 1 and 0) with a world scale vector (any number at all). Before doing my simple vector maths, I needed to multiply the unit vector with a magnitude to turn it into world space. The magnitude was simply the distance between camera and the building.
I can FINALLY move on with more interesting stuff now! Here's the new shader with some natty building placement.
So I've been fussing with the shades the last couple of days and I am trying to discern if I should make one shader that can do all the things that a building needs (hover, selected, can't place) or if I should make different shaders for each state. I think the "one shader" is the way to go but I've put questions out on various sites because I know that big shaders tend to be less efficient than swapping little shaders.
BOOM! Release!
You can rotate the buildings using Q and E (although that's mental, it should be A and D). I might change the highlight edge to red tomorrow morning to make it more obvious when you can't place a building.
Here's my shader! Take a fresnel, tighten it to the edge using a power to the 5, then multiply to strength it, stick it in emission and add a bunch of bools to turn it on and off. Not so bad in the end.
Ah fresnel the magic behind PBR
I didn't know much about PBR but this article by Joe Wilson set me straight.
Oh and I simplified the shader this morning, taking out the hover code (it didn't add much to the playability and added extra raycast cost). It simplified it a bit and still works a treat. I also colour the fresnel red when trying to place the building in an illegal place. Took 6 minutes (I timed it) including testing. Love the shader graph!