Unity Game - A Midnight Snack (by me)

Egan

aa
Feb 14, 2010
1,375
1,720
Hi, I made a game called A Midnight Snack available for download here.

This is my first attempt at making a game in unity, it was for a school project which started in October (I only really worked hard on it in the last 3 weeks). I learned a lot and figured I'd make a post about beginner stuff for unity since there's a lot of weird annoying things that you only learn after hours of figuring out why something doesn't work.


Lightmapping:
  • All lights in unity start off dynamic, and your environment lighting from your sun will eat the hell out of your fps if you have a large terrain. Have to set the terrain lighting to static. Can also set the environment lighting to baked only for a hard limit but then that requires tweaking (in MS I use two environment lights, one for baked - static props - and one for dynamic - viewmodel).
  • http://docs.unity3d.com/Manual/Lightmapping.html Check out 'Tweaking Bake Settings' for easy better-looking lightmaps.
Camera Controls:
  • You can hold right click to move the scene camera with the WASD keys, like in hammer.
3DS Max:
  • You have to bake animation into .fbx files and turn on 'resample' (whatever that means), or else the animations won't port into unity.
  • Name things in your 3ds max files appropriately. every single separate mesh. or else you'll end up with 100 'Cylinder1's all constituting different things confusingly.
  • It's easier to combine animations from within unity, rather than combining them just with 3dsmax (I'm sure some 3dsmax pros would argue otherwise but 3dsmax is labyrinthine for new artists).
Scripting:
  • After you're done editing code, and when you click back into the main window, let the scene reload before clicking your play button or you risk breaking the reloaded scripts (they won't show up in the test, and you'll have to make a new edit and re-reload).
  • I think, name your variables very specific or you'll be confused looking at the large list of vague variable names in the side view (I know, sounds obvious, but I mean really specific like ScriptName_ObjectSpeed rather than just ObjectSpeed).
  • You can set something to not be active by using FindObject("objectname").SetActive(false); but if you want to turn it back on, you can't use the same method (you'll have to associate a variable with it, since FindObject() doesn't work with inactive objects).
  • Most basic commands are the same between JS and C# since they use the same unity libraries, the differences become more extreme when you want more complex code, in which case JS doesn't cut it (but it's fine for basic things).
  • Your GUI elements can only be drawn from within the OnGui() function, so just control when the stuff is drawn with booleans, which are controlled by the other parts of the script.
  • You can use the WaitForSeconds() function call for easy time delays, but you can only use this in your own created functions (can't put a delay in your update() function or a collision() function).
General:
  • Window - Profiler, for the optimisation tool. Can see what takes up fps or draw calls (the exact objects and budget) for specific times.
  • You can make 3D text look higher res by putting it farther back in the world and increasing it's font size, if you're using it for like main menu text and want to use its collider as a button, you'll have to move its collider into the foreground since you aren't technically colliding with it if its behind things in the scene (seems obvious but it took me two hours).
  • You can make empty game objects and attach other objects onto it - underneath it - to use as folders in the Hierarchy tab.
  • You should make a 'Working' folder in the same directory as the Unity folder for your game, which holds all the 3dsmax scene files, and original copies of the fbx / texture files. And then import those into the Unity files. Just in case you ruin something and need a backup copy.
Tests:
  • What I thought was super obvious was the last thing people tried when testing the level. In the cave there are rocks you can hide behind the sight of the bear from, but 5/6 people I saw run my level all tried to run behind the bears back instead of the rocks, and got fed up when that wasn't working. So take into consideration that people like to do things their own way.
  • People judge your stuff based on what they are good at. An artistic friend of mine critiqued my level kinda hard about the basic-look of the models (peer review mark = 75%), but I thought it was great in the gameplay sense since I judge gameplay over aesthetics, thinking that was more important.


Also, it took me a really long time to get the raycast gun to work, so here's the code I used if you also are ever having trouble (written in JS):

The bear death code:
Code:
#pragma strict
var target : GameObject;
var target2 : GameObject;
var bearhealth : int = 3;
var drawKilledBear = false;
var drawShotBear = false;
public var bearPrefab : GameObject = GameObject.Find("Bear_Death_Prefab");
var killedBearAudio : AudioClip;
var bearKilledImage : Texture = Resources.Load("killedbear") as Texture;

function Start () {
// test
bearPrefab = GameObject.Find("Bear_Death_Prefab");
bearKilledImage = Resources.Load("killedbear") as Texture;
}

function Update () {

}

function Kill () {
	Debug.Log ("Shot the bear!");
	//bearPrefab = GameObject.Find("Bear_Death_Prefab");
	bearhealth--;
	Debug.Log("bearhealth: " + bearhealth);
	
	drawShotBearFlash();
	
	if (bearhealth == 0){
		target = GameObject.Find("anim_bear_dead");
		target2 = GameObject.Find("anim_bear_running");  
		target.transform.position = target2.transform.position + Vector3(0,3,0);
		target = GameObject.Find("anim_bear_running");
		
		arriveAtCave.EnableExtraFish = false; // what? somehow this breaks the rest??
		arriveAtCave.maxFishCount = 3;
		
		//Instantiate(bearPrefab, target.transform.position, Quaternion.identity);
		target.transform.position = Vector3(0, 0, 0);
		AudioSource.PlayClipAtPoint(killedBearAudio, Vector3(0,0,0), 0.5f);
		yield WaitForSeconds (0.33);
		drawKilledBear = true;
		yield WaitForSeconds (3); 
		drawKilledBear = false;
	}
	
}

function drawShotBearFlash(){
	drawShotBear = true;
	yield WaitForSeconds (0.33);
	Debug.Log ("!");
	drawShotBear = false;
}

function OnGUI () {
	if (drawKilledBear == true){
		GUI.DrawTexture (new Rect(Screen.width/2-110,Screen.height - 90, 220, 35), bearKilledImage);
	}
	if (drawShotBear == true){;
		GUI.Label (Rect (Screen.width/2-25, Screen.height/2-25, 50, 50), bearhealth.ToString() + "/3");
	}
}

The raycast code:
Code:
#pragma strict

var target : ParticleSystem;
internal var weapon_target : GameObject;
internal var gunTarget : GameObject;
var gunshotAudio : AudioClip;
var gunemptyAudio : AudioClip;
var DelayTime : float = 0.0f;

static var canshootgun = true;

var test = false;

function Start () {
	weapon_target = GameObject.Find("prop_chair_01");
}

function Update () {
	DelayTime -= Time.deltaTime;
	if (Input.GetMouseButtonDown(0) && canshootgun) {
		// bump up cooldown remaining to 1
		if (PickupGun.ammo > 0 && PickupGun.HasGun == true && RunAndCrouch.IsSprinting == false && DelayTime <= 0)
		{
			PickupGun.ammo--;
			DelayTime=1.1;
			AudioSource.PlayClipAtPoint(gunshotAudio, Vector3(0,0,0));
			FireGunParticle.PlayParticle = true;
			gunTarget = GameObject.Find("anim_gun_01"); 
			gunTarget.animation.Play("Fire");
			var ray = Camera.main.ScreenPointToRay(new Vector3(Screen.width/2,Screen.height/2));
			// var up = transform.TransformDirection(Vector3.up);
			var fwd = transform.TransformDirection (Vector3.forward);
			var ObstacleHit : RaycastHit;
			//if (Physics.Raycast (transform.position, fwd, 1000) && ObstacleHit.collider.tag == "bear") {
			if (Physics.Raycast (ray, ObstacleHit, 5000)) { 
            	print ("Shot gun");
            
            	
            	var bearDeathScript : BearDeathScript = ObstacleHit.collider.GetComponent(BearDeathScript);
			    if (bearDeathScript != null) {
			        bearDeathScript.Kill();
			    }
            	
            	//Vector3 hitPoint = hitInfo.point; // set up hitpoint
				//if (hitInfo.collider.gmaeObject == thebear )
					// you hit the bear
					// bear health --;
        	}
			
   			// var hit : RaycastHit;    
  			// Debug.DrawRay(transform.position, -up * 10, Color.green);
  			
		}
		// IF AMMO IS EMPTY:
		if (PickupGun.ammo <= 0 && PickupGun.HasGun == true && RunAndCrouch.IsSprinting == false && DelayTime <= 0)
		{
			AudioSource.PlayClipAtPoint(gunemptyAudio, Vector3(0,0,0), 0.5f);
			DelayTime=1.1;
		}
		
		//target = ParticleSystem.Find("gun_particle"); 
		//target.Play;
		
	}
	
	if (DelayTime < -5)
		DelayTime = -5;
}

There's probably a way better way to target variables and objects in other places but this target thing was the most reliable for me.
 
Last edited by a moderator:

HQDefault

...what
aa
Aug 6, 2014
1,056
535
:|
You lose points for the long and pointless trek. I'll be fair since it's an earlier project, but still.
 

Egan

aa
Feb 14, 2010
1,375
1,720
:|
You lose points for the long and pointless trek. I'll be fair since it's an earlier project, but still.

Yeah regularly (if you aren't a pro shot like me) you just follow the bear back to its cave, where it adds two more fish to pick up. And there at least is a sprint function (and the secret fast forward button). But I do agree it's longer than it needs to be.