I’ve gone blind on this, but basically I have a first-person player who should be able to build stuff in the game by lying out floor tiles and then “attach” walls etc. to those tiles.
My problem is the math required to calculate the positioning of wall once they are close to a floor tile, as visualized here (you can see the floor tile, the green line is the wall the player is “holding”, and the red line is a Debug.DrawRay):
Here’s the part of my script, and please notice that I make the wall a child of the floor when it comes close enough:
// Update is called once per frame
void Update () {
// Do we have an object?
if ( itemObject == null ) {
return;
}
// Are we holding an object?
if ( isHoldingObject ) {
// Let the itemObject follow the camera
itemObject.transform.rotation = new Quaternion( 0.0f, playerCamera.transform.rotation.y, 0.0f, playerCamera.transform.rotation.w );
itemObject.transform.position = playerCamera.transform.position + playerCamera.transform.forward * distanceFromCamera;
// Are there any other objects close to this itemObject that it can connect to?
GameObject nearestObject = FindNearestConstructableObject( itemObject );
// Do we have a constructable object close enough?
if ( nearestObject != null ) {
itemObject.renderer.material.color = legalPositionColor;
// Make itemObject the child of nearestObject
itemObject.transform.parent = nearestObject.transform;
// Snap itemObject to nearestObject
if ( currentItem.name == "Wall" ) {
Debug.DrawRay( itemObject.transform.position, itemObject.transform.forward, illegalPositionColor );
//
// WHAT SHOULD I DO HERE?!
//
}
}
else {
itemObject.renderer.material.color = illegalPositionColor;
// Unparent
itemObject.transform.parent = null;
}
// "Let go" of the object
if ( Input.GetButtonDown("Fire1") ) {
inventory.RemoveItem( currentItem );
isHoldingObject = false;
itemObject.renderer.material.color = origItemColor;
}
}
}
If you parent the wall under the tile,
and set it’s rotation to zero,
and position to zero,
and after that move the wall half a tile in either x or z it will align correctly.
What I wrote was for snapping to the tile, having the player no chance to influence its rotation. You could do a raycast and see where your camera hits the tile and then choose a rotation and position out of that.
If you want to rotate the wall as the camera rotates you need to have it as child under the camera. You cant have both in an easy way
Id go for raycast to hit the tile, look where you hit it and then place the wall as I stated before but different in position and rotation depending on where you hit the tile.
Edit: Alternativly rotate the wall in it’s local-space by 90 if you press a button in combination with my prev. post.
The itemObject already follows the camera’s rotation, without being a child of it, ref. “Let the itemObject follow the camera” in the code above.
I have thought about using raycasts, but they won’t work in the long run; let’s say I want to put up a wall on the third floor, where a raycast would go into nothing. Therefore I look for the nearest objects already there, so that if I have two pillars, I can put up a wall in between them (not caring about any floor in that case).
This is getting frustrating, because in my head I feel I’m pretty close.
On every Update() I check if the itemObject is close to another “constructable” object, in this case the floor tile. If it is, I set the itemObject to be a child of the nearestObject. I also zeroes out itemObject’s position and rotation in this case, so that the parent/child are relative to each other.
Now.
I know there must be a way to find out the relative x/z angle between these two object as I move around with the itemObject “in hand”, and that with that “angle data” (or whatever) can decide a fixed 90-degrees rotation (and position).
If your tile has an pivot in the middle then create a vector3 between where your raycast hit and the middle of the tile. Depending on this vectors normalized values (1 to -1) you can calculate how you are supposted to rotate the wall. If you are afraid of having alot of objects ontop of eachother just tag all tiles with “tile” and do a RaycastAll, where you find the topmost “tile”.
One thing I’m thinking about now, is that I could probably have a raycast going out of a wall’s, floor’s, whatever, all sides. And by that I can see if a wall is just between two pillars. Or something. hmm
I have looked into a solution where I do six raycasts (from each object’s direction) to find nearby (and matching) objects that the current object can attach to, but that makes things just harder, as I see it. A Physics.Overlapsphere seems a lot more logical to me, but feel free to prove me wrong.
I think I have thought about the same thing. Are you suggesting that each tile has four “ghost tiles” (as I like to call it) attached to it in all four directions (all but up and down)?
But doesn’t this mean that a scene with lots of building will have a lot of overhead?
this might do the trick, given that the wall and floor have variables defining their width, height, depth and are centered.
And I’m not misunderstanding the problem
//attach wall to floor, easier to deal with local positions
wall.transform.parent = floor.transform;
//find possible positions by comparing directions
positions = new List<Vector3>();
if (Vector3.Dot(wall.localPosition, Vector3.forward) > 0) {
positions.Add(Vector3.forward * floor.depth / 2);
}
if (Vector3.Dot(wall.localPosition, Vector3.right) > 0) {
positions.Add(Vector3.right * floor.width / 2);
}
if (Vector3.Dot(wall.localPosition, -Vector3.right) > 0) {
positions.Add(-Vector3.right * floor.width / 2);
}
if (Vector3.Dot(wall.localPosition, -Vector3.forward) > 0) {
positions.Add(-Vector3.forward * floor.depth / 2);
}
//find best position
float bestDistSqr = float.PositiveInfinity;
Vector3? bestPosition = null;
foreach (Vector3 position in positions) {
float distSqr = (position - wall.transform.localPosition).magnitudeSqr;
if (distSqr < bestDistSqr) {
bestDistSqr = distSqr;
bestPosition = position;
}
}
//place wall at best position
wall.transform.localPosition = bestPosition.Value + Vector3.up * wall.height / 2;
you could also check the walls facing direction if you need walls to be placed with a certain orientation:
if (Vector3.Dot(floor.transform.InverseTransformDirection(wall.forward), Vector3.forward) < 0) {
//add the forward position if the wall is facing it
}
Here’s the code I ended up with, but it still doesn’t work. The new position is nowhere close to the nearestObject’s position (ie. the floor’s position).
// Parenting
itemObject.transform.parent = nearestObject.transform;
// Find possible positions by comparing directions
List<Vector3> positions = new List<Vector3>();
if ( Vector3.Dot (itemObject.transform.localPosition, Vector3.forward) > 0 ) {
positions.Add ( Vector3.forward * nearestObject.collider.bounds.size.z / 2 );
}
if ( Vector3.Dot (itemObject.transform.localPosition, Vector3.right) > 0 ) {
positions.Add ( Vector3.right * nearestObject.collider.bounds.size.x / 2 );
}
if ( Vector3.Dot (itemObject.transform.localPosition, -Vector3.right) > 0 ) {
positions.Add ( -Vector3.right * nearestObject.collider.bounds.size.x / 2 );
}
if ( Vector3.Dot (itemObject.transform.localPosition, -Vector3.forward) > 0 ) {
positions.Add ( -Vector3.forward * nearestObject.collider.bounds.size.z / 2 );
}
// Find best position
float bestDistSqr = float.PositiveInfinity;
Vector3? bestPosition = null;
foreach ( Vector3 position in positions ) {
float distSqr = ( position - itemObject.transform.localPosition ).sqrMagnitude;
if ( distSqr < bestDistSqr ) {
bestDistSqr = distSqr;
bestPosition = position;
}
}
// Place wall at best position
itemObject.transform.localPosition = bestPosition.Value + Vector3.up * itemObject.collider.bounds.size.y / 2;
Anything obviously wrong here?
UPDATE: One obvious thing I see, is that bestDistSqr is never being used for anything.
Here’s a screenshot from the scene view, which seems to illustrate that the wall is being snapped into place “one floor tile” away from the floor tile towards the player.