Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411711 Posts in 69402 Topics- by 58456 Members - Latest Member: FezzikTheGiant

May 20, 2024, 11:22:06 PM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsCommunityDevLogsPolyhedra - Procedurally Generated FPS
Pages: [1]
Print
Author Topic: Polyhedra - Procedurally Generated FPS  (Read 1417 times)
Lycaon
Guest
« on: November 30, 2014, 04:25:00 PM »

Hi. I'm making something small (maybe) and strange (definitely). It's called "Polyhedra", you'll see why in a moment.

You can play it here.

The game is made in Unity. Everything exceptthe health pickup models are procedurally generated (those I made myself). The enemies are made by combining the meshes of a bunch of randomly placed and scaled cube primitives. The levels are made with cellular automata, specifically a version of Conway's Game of Life where dead cells can't come to life. The skybox is random colored noise, the wall and floor textures are greyscale noise given a colored tint.

Screenshots:







And there's a video of some gameplay



Any bug reports, suggestions, death threats, glowing praise, or interesting philosophical questions can be directed to
@LycaonTalks on Twitter.
« Last Edit: November 03, 2016, 08:31:46 PM by Emma » Logged
Donutttt
Level 0
**



View Profile
« Reply #1 on: November 30, 2014, 05:05:58 PM »

I like the style, my depth perception was going crazy watching the video. Nice to see experimental stuff in first person, I'll keep an eye on this log.
Logged
Lycaon
Guest
« Reply #2 on: December 01, 2014, 11:47:41 AM »

Alpha 4 is out! You can play it here.

Alpha 4 Changelog

  • Added gun firing animation
  • Added gun firing noise
  • Added enemy death sound
  • Added player hit sound
  • Added screenshake on player hit
  • Cubes explode into fragments on death
  • Added varying speeds for melee cubes
  • Ranged cubes can only attack when within a certain range (instead of just being based on line of sight)
  • Player death now goes to game over screen
  • Levels now have exits that go to another level
  • Changed player movement to be rigidbody based (you can no longer horse yourself through corners)

And now the detail on some of the more interesting points on that list!

The sound effects were all made with the lovely sfxr. I may replace them with something more professional in the future, but for now I like them.

The firing "animation" is kind of cheating, becuase I don't actually know how to use 3D modeling software to animate things, so instead I just use a coroutine to rotate the gun to look like it's recoiling and resetting back to its resting position.

Code:
IEnumerator firingAnimation(){
Transform gun = transform.GetChild(3);

bool recoil = true;
bool reset = false;

while(recoil){
yield return null;
gun.Rotate(new Vector3(3, 0, 0));

if(gun.rotation.eulerAngles.x <= 270){
recoil = false;
reset = true;
}
}

while(reset){
yield return null;
gun.Rotate(new Vector3(-1, 0, 0));

if(gun.rotation.eulerAngles.x >= 300){
reset = false;
}
}

}



The screenshake is done with a coroutine that simply sets the camera position to a random position in a sphere then resets it to the position it was at when it started. Simple solution, I'm quite fond of it.

Code:
IEnumerator ScreenShake(float shake, float shakeAmount, float decreaseFactor){
Vector3 startPos = Camera.main.transform.localPosition;

while(shake > 0){
Camera.main.transform.localPosition = startPos + Random.insideUnitSphere * shakeAmount;
shake -= Time.deltaTime * decreaseFactor;
yield return null;
}

Camera.main.transform.localPosition = startPos;


}

So, the cubes are all made by creating a bunch of cube primitives and then using CombineMeshes() to create a single thing to render and scale and stuff. The way I do this leaves the original cube primitives as inactive children of the final product, so when the cube dies I just re-activate the cubes, de-parent them from the dead cube, and then apply explosive horse.

Code:
foreach(Transform child in transform){
child.gameObject.SetActive(true);
child.transform.parent = null;
child.transform.position = transform.position;
var rigid = child.gameObject.AddComponent<Rigidbody>();
rigid.AddExplosionForce(Random.Range(25f, 100f), transform.position, 0, 3.0f);
Destroy(child.gameObject, 5f);
}


The exit placement is generated the same way as the player placement is, randomly picking points on the map until you get an empty one.

Lastly, the player movement is being handled using rigidbody.AddForce() instead of Transform.Translate(), so that you can't walk into corners and launch yourself across the map anymore.

Code:
rigidbody.velocity = new Vector3(0, rigidbody.velocity.y, 0);

float horizontal = Input.GetAxis("Horizontal");
float vertical = Input.GetAxis("Vertical");

if(horizontal != 0 || vertical != 0){
GunBobbing.isWalking = true;
}
else{
GunBobbing.isWalking = false;
}

Vector3 dir = new Vector3(horizontal, 0, vertical);
dir *= Time.deltaTime;
dir *= speed;

dir = transform.TransformDirection(dir);

rigidbody.AddForce(dir);

Thanks for reading my nonsense ramblings, I hope you enjoy the game!
Logged
Lycaon
Guest
« Reply #3 on: December 01, 2014, 02:50:28 PM »

I've got a gif for you! Most of the new features in Alpha 3, plus a couple of the things I'm working on for Alpha 4. To save bandwidth I'm hiding the gifs behind screenshots from them. Click to see the full thing!

Logged
Lycaon
Guest
« Reply #4 on: December 02, 2014, 03:09:15 PM »

Alpha 4 is live! And I'm charging for the game now! That's scary! If you're press and want a review copy of the game, email [email protected] and we can chat.

On to the changelog!

Alpha 4 changelog

  • Minimap is now more zoomed in on the player
  • Player minimap icon now shows orientation
  • Set brightness limits on the level textures so you can't get a level that's too dark to see in
  • Added health pickups
  • Difficulty now scales with level (enemies do more damage and have more health the more levels you go through)
  • Added level counter to GUI
  • Holding tab now shows the whole map

Alpha 5 will probably take some time to come out, because my plan is to introduce randomized weapons, like the ones found in Borderlands. That's gonna be difficult. I'll post in this thread as I go, however.

Now, onto the elaboration!

The minimap now starts zoomed in so you can see player orientation (because I changed the minimap icon from a circle to a triangle), but you can hit tab to see the full map at anytime. Essentially, the minimap is a camera that only renders the minimap tiles (which are spawned above each wall object at generation time, creating a sort of 2D map floating above the real map). Its clear flags are also set to "Don't Clear". Hitting tab increases the size from 50 to 100, and sets the viewport rect to (0, 0, 1, 1).

Code:
if(Input.GetButton("Map")){
minimap.orthographicSize = 100;
minimap.rect = new Rect(0f, 0f, 1f, 1f);
minimap.transform.position = new Vector3(100, 20, 100);
minimap.gameObject.GetComponent<FollowPlayer>().enabled = false;

}
else{
minimap.orthographicSize = 50;
minimap.rect = new Rect(0.8f, 0.7f, 0.5f, 0.5f);
minimap.gameObject.GetComponent<FollowPlayer>().enabled = true;
}

Sometimes levels get generated where the wall textures are really dark, which makes it hard to see. I attempted to fix this by adding a brightness limit - the sum of the rgb values for the colors can't be less than 0.6, which solves this problem nicely.

Health pickups are pretty straighforward - you walk into them when you're not at full health, and they heal you. The bigger the health pickup, the more they heal you by. Right now the pickups are generated with the world, and don't drop from enemies, but that may change in the future.

And that's basically all the interesting stuff I did this update. For sitting through that, have a demo video!



(Stupid tigsource BBCode, not letting me embed youtube videos)
« Last Edit: December 02, 2014, 03:41:32 PM by Lycaon » Logged
Lycaon
Guest
« Reply #5 on: December 02, 2014, 09:22:51 PM »

I've got a rough idea of the gun generation working! Needs some polish, though. Once it's all done I'll do a big post about how it works.



Logged
Lycaon
Guest
« Reply #6 on: December 03, 2014, 06:43:20 PM »

Alpha 5 is out! That took less time than I expected. And, I've changed the name from the probably-stupid "The cubes are breathing" to "Polyhedra"! You can play it here. Any feedback is much appreciated!


Alpha 5 Changelog

  • Added randomized weapons
  • Adjusted enemy level scaling factor (gets more difficult more quickly now)

Now, onto what you all really care about - the guns! I was originally going to just have a  bunch of prefab gun parts that could be stuck together randomly, but my friend @ignifluous suggested re-purposing the random mesh generation code that I use for the enemies to make the weapons. So, that's exactly what I did. As such, this will sort of serve double duty as a primer on how I generate the enemy models and the weapons.

First off, we need a class to hold the attributes for the weapons. These play into both the final stats of the guns, and also how they look. That data is stored in a class called WeaponAttributes, which has three variables - speed, weight, and accuracy. Speed is the fire rate of the weapon, weight determines the damage, and accuracy doesn't do anything yet but I'm working on it.

Now, the actual mesh is generated by first creating an empty GameObject and deciding how many cubes the model is going to be made out of. This is done by getting a random number between 4 and 4+(10 - weapon.speed). This means that faster guns have fewer cubes in them. Then the magic happens.

Code:
for(int i = 0; i < numCubes; i++){
GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
cube.transform.localScale = new Vector3(Random.value * weapon.weight, Random.value * (weapon.accuracy + weapon.weight), Random.value * weapon.weight);
cube.transform.parent = gun.transform;
cube.transform.position = new Vector3(Random.Range(-0.2f, 0.2f) / weapon.accuracy, i * gun.transform.localScale.y, Random.Range(-0.2f, 0.2f) / weapon.accuracy);
}

Basically, we create a bunch of cube primitives, then set them to random sizes. The x value is the width, the y value is the length, and the z value is the height. This means that higher-damage guns are bigger, while lower-damage guns are smaller. More accurate guns are also longer. After that, we simply set the positions - the x and z coordinates are mostly random, but get less variant the more accurate the gun is. Each cube is placed a a certain distance from the last, so you don't get guns that are just one giant cube. Or, at least, you usually don't get guns that are one giant cube.

After that, we use Unity's handy CombineMesh feature to fuse all the bits into one mesh, to cut down on draw calls and make a few things easier.

Code:
MeshFilter[] meshFilters = gun.GetComponentsInChildren<MeshFilter>();
CombineInstance[] combine = new CombineInstance[meshFilters.Length];

for(int i = 0; i < meshFilters.Length; i++){
combine[i].mesh = meshFilters[i].mesh;
combine[i].transform = meshFilters[i].transform.localToWorldMatrix;
meshFilters[i].gameObject.active = false;

}


gunMeshFilter.mesh = new Mesh();
gunMeshFilter.mesh.CombineMeshes(combine);

Then we set the material to a random color, and add a component to the finished gun that holds its damage, fire rate, and accuracy.

The stats for the guns are made by first generating a random damage value between 1 and ten, then calculating the fire rate with the formula frt = (10 - dmg) + Random.Range(-1f, 1f). This makes it so that high damage guns have a lower fire rate, and low damage guns have a high fire rate, with some random variance in there for flavour. The accuracy is just a random number form 1 to 10 until I figure out exactly what I want it to do.

And that's it, really. I doubt that this will be useful to a ton of people, because I don't know how much demand there is for scripts that generate random, vaguely gun-like shapes made from cube primitives, but I hope it was interesting. Thanks for reading!




 

Logged
Pages: [1]
Print
Jump to:  

Theme orange-lt created by panic