It's been a while since I posted any code.. Here are some handy methods that I've been using frequently in my SlingBots game. Keep in mind, some examples use local variables where it's better to reuse variables with more scope for memory/GC reasons in practical application. If you see something that I could really improve, please let me know. Many of these are "setup" routines so I've not bothered with optimization really at all since they run once, or very infrequently.
All of these are designed to work with the standard Unity orientation, with normal terrain objects or terrain meshes.
RayCast Position of Ground at any point on terrain:
Vector3 GroundPosAt(Vector3 position)
{
Vector3 result = position;
//some altitude above the highest possible ground position.
float checkAltitude = 2000.0f;
Ray rr = new Ray(new Vector3(position.x, checkAltitude, position.z), -Vector3.up);
//I use a fixed allocation ray cast as a general practice to keep GC down.
//It will need to be big enough to contain all the possible hits it could catch finding your ground object..
//YMMV with these *NonAlloc methods, but they work wonderfully for me.
RaycastHit[] hits = new RaycastHit[20];
if (Physics.RaycastNonAlloc(rr, hits) > 0)
{
foreach(RaycastHit hit in hits)
{
//make sure this entry isn't null
if (hit.collider != null)
{
//check for collision with object tagged ground
if (hit.collider.CompareTag("ground"))
{
result = hit.point;
}
}
}
}
return result;
}
Get A Vector3 position on a Circle at a specific angle and radius:
Vector3 CirclePos(Vector3 center, float radius, float angle)
{
Vector3 pos;
pos.x = center.x + radius * Mathf.Sin(angle * Mathf.Deg2Rad);
pos.y = center.y;
pos.z = center.z + radius * Mathf.Cos(angle * Mathf.Deg2Rad);
return pos;
}
Object Placement using fixed arrays for object tracking and management:
This example shows one of the ways I programmatically instantiate turrets into SlingBot Boarding.
public GameObject worldTurretPrefab;
GameObject[] worldTurrets = new GameObject[100];
SnowBallTurret[] _worldTurrets = new SnowBallTurret[100];
void PlaceWorldTurret(GameObject parentobject, Vector3 position, int firefrequency, float firepower, float sightdistance)
{
for (int i = 0; i < worldTurrets.Length; i++)
{
if (worldTurrets[i] == null)
{
if (parentobject == null)
{
worldTurrets[i] = Instantiate(worldTurretPrefab);
}
else
{
worldTurrets[i] = Instantiate(worldTurretPrefab, parentobject.transform, false);
}
_worldTurrets[i] = worldTurrets[i].GetComponentInChildren<SnowBallTurret>();
_worldTurrets[i].playerAvatar = GameController.GControl.Player;
_worldTurrets[i].fireFrequency = firefrequency;
_worldTurrets[i].id = (i + 1).ToString();
_worldTurrets[i].turretProjectileVelocity = firepower;
_worldTurrets[i].turretSightingDistance = sightdistance;
worldTurrets[i].transform.localPosition = position;
break;
}
}
}
I use an array for both the object and a reference to the script it holds to save time on future getcomponent lookups. This will come in handy when I want to upgrade the settings on all of the already instantiated objects due to a player increase in level/etc.
I use a fixed array so I can predict(reasonably) what the upper level of memory usage will be(after testing). I iterate through the existing collection and only add a new object if there is an empty slot in the array. This allows me to ensure there will be ZERO runtime exceptions related to object instantiation. It is better for my game to end up being a little easier than it should have been than it would be for an exception to be thrown right at an exciting moment. Note: the above method could be easily modified to return a bool on success/failure if you wanted to adjust the object arrays and try again on failure.
Putting this all together, here's instantiating Turrets in a circle around a specific point(on uneven terrain):
void PlaceTurretsCircle(Vector3 position, int turretcount, float turretradius)
{
//place turrets in circle
for (int i = 0; i < turretcount; i++)
{
//Adjust settings based on players level
float levelModifier = 1.0f;
if (currentGameLevel > 1)
{
//add 20% to modifier for each level
levelModifier += (0.2f * currentGameLevel);
}
//Calculate angle for object around circle
float angl = (360 / turretcount) * i;
PlaceWorldTurret(null,
GroundPosAt(CirclePos(position, turretradius, (360 / turretcount) * i)),
(int)(2000.0f / levelModifier),
50.0f * levelModifier,
500.0f);
}
}
and a bonus, Here's placing turrets on a Grid:
This one is presently written to require a parent object as it places the turrets in relative position to that object.
void PlaceTurretsGrid(GameObject parentobject, float xstart, float zstart, float xrowdist, float zrowdist, float yoffset, int xrowcount, int count, int firefrequency, float firepower, float sightdistance)
{
float xoffset = xstart;
float zoffset = zstart;
if (count > 100) count = 100;
int xmaxcount = xrowcount - 1;
int xcount = 0;
for (int i = 0; i < count; i++)
{
//Without ground position checking
PlaceWorldTurret(parentObject, new Vector3(xoffset, yoffset, zoffset), turretFireFrequency, turretFirePower, turretSightDistance);
//With ground position checking(untested)
//PlaceWorldTurret(parentObject, GroundPosAt(parentObject.position + new Vector3(xoffset, yoffset, zoffset)), turretFireFrequency, turretFirePower, turretSightDistance);
xcount++;
xoffset += xrowdist;
if (xcount > xmaxcount)
{
xcount = 0;
xoffset = xstart;
zoffset += zrowdist;
}
}
}
Not a lot of rocket science going on here, but it could be a time-saver or a mental-block fixer for somebody I'm sure.
Check out the game if you get a chance:
https://www.kongregate.com/games/WilliamOlyOlson/slingbot-boarding/
Happy coding out there!!
Thanks for sharing.