mirror of
https://github.com/tetreum/brickcraft.git
synced 2026-02-09 11:38:59 -06:00
279 lines
11 KiB
C#
279 lines
11 KiB
C#
using UnityEngine;
|
|
using Brickcraft.UI;
|
|
using Brickcraft.World;
|
|
|
|
namespace Brickcraft
|
|
{
|
|
public class BrickCollisionDetector : MonoBehaviour
|
|
{
|
|
public static BrickCollisionDetector Instance;
|
|
|
|
[HideInInspector]
|
|
public static Transform pivot;
|
|
|
|
[HideInInspector]
|
|
public int currentBrickType;
|
|
|
|
private Material m;
|
|
private bool isColliding = false;
|
|
private Vector3 lastPos;
|
|
private Vector3 currentStud;
|
|
private Transform latestStudGrid;
|
|
private Vector2Int latestStud;
|
|
private bool isHighAgainstTerrain = false;
|
|
|
|
private void Awake() {
|
|
Instance = this;
|
|
|
|
int ignoreRayCastLayer = (int)Game.Layers.IgnoreRaycast;
|
|
|
|
// We need to set a parent object so we can properly rotate the bricks
|
|
// as well as place them using a stud position rather than it's center pos
|
|
if (pivot == null) {
|
|
pivot = new GameObject().transform;
|
|
pivot.name = "Previewer";
|
|
pivot.gameObject.layer = ignoreRayCastLayer;
|
|
}
|
|
transform.SetParent(pivot);
|
|
pivot.position = new Vector3(0, 999, 0); // send it far away from player view while not in use
|
|
|
|
// lower it's scale, so we don't trigger false collision positives
|
|
// with nearby bricks
|
|
transform.localScale = new Vector3(0.999f, 0.999f, 0.999f);
|
|
gameObject.layer = ignoreRayCastLayer;
|
|
|
|
foreach (Transform trans in gameObject.GetComponentsInChildren<Transform>()) {
|
|
trans.gameObject.layer = ignoreRayCastLayer;
|
|
}
|
|
|
|
Renderer renderer = GetComponent<Renderer>();
|
|
// disable shadows to make it easier to see when placing
|
|
renderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
|
|
renderer.sharedMaterial = Game.Instance.transparentMaterial;
|
|
m = renderer.sharedMaterial;
|
|
|
|
Collider[] colliders = GetComponents<Collider>();
|
|
|
|
foreach (Collider collider in colliders) {
|
|
collider.isTrigger = true;
|
|
}
|
|
|
|
Rigidbody rigid = gameObject.AddComponent<Rigidbody>();
|
|
rigid.isKinematic = true;
|
|
setValid(false);
|
|
}
|
|
|
|
public void setCurrentBrickType(int brickType) {
|
|
currentBrickType = brickType;
|
|
transform.localPosition = Server.brickModels[currentBrickType].pivot;
|
|
}
|
|
|
|
private void Update() {
|
|
if (PlayerPanel.Instance.selectedItem == null ||
|
|
PlayerPanel.Instance.selectedItem.item.type != Item.Type.Brick) {
|
|
return;
|
|
}
|
|
if (Input.GetAxis("Mouse ScrollWheel") != 0f && lastPos != Vector3.zero && latestStudGrid != null) {
|
|
Vector3 rot;
|
|
if (latestStudGrid.localRotation == Quaternion.identity) {
|
|
rot = Input.GetAxis("Mouse ScrollWheel") > 0f ? Vector3.up : Vector3.down;
|
|
} else {
|
|
rot = Input.GetAxis("Mouse ScrollWheel") > 0f ? Vector3.forward : Vector3.back;
|
|
}
|
|
pivot.RotateAround(currentStud, rot, 90);
|
|
}
|
|
if (Input.GetMouseButtonDown(1) && !isColliding) {
|
|
Vector3 pos = transform.position;
|
|
pos.y -= isHighAgainstTerrain ? 0.001f : 0.006f; // ugly hack to lower bricks pos
|
|
Server.Instance.spawnBrick(PlayerPanel.Instance.selectedItem.item, pos, transform.rotation);
|
|
|
|
Player.Instance.removeItem(new UserItem() {
|
|
id = PlayerPanel.Instance.selectedItem.id,
|
|
health = PlayerPanel.Instance.selectedItem.health,
|
|
quantity = 1
|
|
});
|
|
|
|
resetVars();
|
|
}
|
|
}
|
|
|
|
private void resetVars() {
|
|
latestStudGrid = null;
|
|
latestStud = Vector2Int.zero;
|
|
}
|
|
|
|
void OnTriggerStay(Collider other) {
|
|
// ignore collisions with studs
|
|
if (other.name.StartsWith("GridStud")) {
|
|
return;
|
|
}
|
|
|
|
// prevent resetting those vars each frame
|
|
if (isColliding) {
|
|
return;
|
|
}
|
|
|
|
isColliding = true;
|
|
setValid(false);
|
|
}
|
|
|
|
void OnTriggerExit(Collider other) {
|
|
isColliding = false;
|
|
setValid(true);
|
|
}
|
|
|
|
private StudInfo hitPointToStud(RaycastHit hit) {
|
|
// time to understand at which Stud is he looking at
|
|
StudInfo stud = new StudInfo();
|
|
|
|
// get grid dimensions
|
|
if (hit.collider.name.StartsWith("GridStud")) {
|
|
string[] tmp = hit.collider.name.Replace("GridStud ", "").Replace("GridStudBottom ", "").Split('x');
|
|
stud.gridDimensions = new Vector2Int(int.Parse(tmp[0]), int.Parse(tmp[1]));
|
|
|
|
isHighAgainstTerrain = false;
|
|
} else {
|
|
// is looking at world stud
|
|
// so grid is a 16x16 (chunk slice) made by 2x2 bricks
|
|
stud.gridDimensions = new Vector2Int(Chunk.SliceHeight * 2, Chunk.SliceHeight * 2);
|
|
isHighAgainstTerrain = true;
|
|
}
|
|
|
|
// 1x1 are easy xD
|
|
if (stud.gridDimensions.x == 1 && stud.gridDimensions.y == 1) {
|
|
return stud;
|
|
}
|
|
|
|
// convert world coords to local ones
|
|
Vector3 localHitpoint = hit.collider.transform.InverseTransformPoint(hit.point);
|
|
|
|
if (isHighAgainstTerrain) { // chunkSlices have center wrongly set
|
|
Vector3 localCenter = hit.collider.transform.InverseTransformPoint(hit.collider.transform.GetComponent<Collider>().bounds.center);
|
|
localHitpoint -= localCenter;
|
|
}
|
|
|
|
// since localHitpoint is based on the center of the object, we need to sum half
|
|
// of it's size
|
|
localHitpoint.x += (stud.gridDimensions.x * Server.studSize) / 2;
|
|
localHitpoint.z += (stud.gridDimensions.y * Server.studSize) / 2;
|
|
|
|
for (int i = 1; i < (stud.gridDimensions.x + 1); i++) {
|
|
if (localHitpoint.x < (i * Server.studSize)) {
|
|
stud.center.x = (i * Server.studSize) - (Server.studSize / 2) - ((stud.gridDimensions.x * Server.studSize) / 2);
|
|
stud.gridPosition.x = (i - 1);
|
|
break;
|
|
}
|
|
}
|
|
for (int i = 1; i < (stud.gridDimensions.y + 1); i++) {
|
|
if (localHitpoint.z < (i * Server.studSize)) {
|
|
stud.center.z = (i * Server.studSize) - (Server.studSize / 2) - ((stud.gridDimensions.y * Server.studSize) / 2);
|
|
stud.gridPosition.y = (i - 1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return stud;
|
|
}
|
|
|
|
public bool isLookingAtWorldStud (RaycastHit hit) {
|
|
Vector3[] vertices = getTriangleVertices(hit.transform.GetComponent<MeshCollider>().sharedMesh, hit.triangleIndex);
|
|
|
|
Vector3Int equal = new Vector3Int(
|
|
vertices[0].x == vertices[1].x && vertices[1].x == vertices[2].x ? 1 : 0,
|
|
vertices[0].y == vertices[1].y && vertices[1].y == vertices[2].y ? 1 : 0,
|
|
vertices[0].z == vertices[1].z && vertices[1].z == vertices[2].z ? 1 : 0
|
|
);
|
|
|
|
// y is the same OR there are no equal values (round triangle) then we know that is top or bottom face
|
|
return vertices[0].y == vertices[1].y && vertices[1].y == vertices[2].y || equal == Vector3Int.zero;
|
|
}
|
|
|
|
Vector3[] getTriangleVertices(Mesh mesh, int triangleIndex) {
|
|
Vector3[] vertices = mesh.vertices;
|
|
int[] triangles = mesh.triangles;
|
|
|
|
Vector3[] output = new Vector3[3];
|
|
|
|
output[0] = vertices[triangles[triangleIndex * 3 + 0]];
|
|
output[1] = vertices[triangles[triangleIndex * 3 + 1]];
|
|
output[2] = vertices[triangles[triangleIndex * 3 + 2]];
|
|
|
|
return output;
|
|
}
|
|
|
|
public void lookingAtStud(RaycastHit hit) {
|
|
// player is not holding a brick
|
|
if (PlayerPanel.Instance.selectedItem == null || PlayerPanel.Instance.selectedItem.item.type != Item.Type.Brick) {
|
|
return;
|
|
}
|
|
|
|
var stud = hitPointToStud(hit);
|
|
|
|
if (hit.transform == latestStudGrid && stud.gridPosition == latestStud) {
|
|
return;
|
|
}
|
|
|
|
Quaternion rot = pivot.rotation; // keep current rotation
|
|
|
|
// if old stud and new stud don't have the same rotation,
|
|
// pivot rotation will be invalid, so we reset it to new stud's rotation
|
|
if (latestStudGrid == null || hit.transform.localRotation != latestStudGrid.localRotation) {
|
|
rot = hit.transform.localRotation;
|
|
}
|
|
|
|
latestStudGrid = hit.transform;
|
|
latestStud = stud.gridPosition;
|
|
|
|
Vector3 studPos = hit.collider.transform.TransformPoint(stud.center);
|
|
|
|
|
|
// height needs to be corrected for bottom studs
|
|
if (hit.collider.name.Contains("Bottom")) {
|
|
studPos.y -= PlayerPanel.Instance.selectedItem.item.brickModel.heightInPlates * Server.plateHeight;
|
|
} else if (isHighAgainstTerrain) { // chunkSlices have center wrongly set
|
|
Vector3 localCenter = hit.collider.transform.InverseTransformPoint(hit.collider.transform.GetComponent<Collider>().bounds.center);
|
|
studPos += localCenter;
|
|
studPos.y = hit.point.y;
|
|
}
|
|
|
|
currentStud = studPos;
|
|
|
|
GameObject brickObj = hit.collider.transform.parent.gameObject;
|
|
|
|
if (!Server.bricks.ContainsKey(brickObj.name) && !hit.collider.name.StartsWith("ChunkSlice")) {
|
|
Debug.LogError("Brick not found in server list " + brickObj.name);
|
|
return;
|
|
}
|
|
|
|
// same model with all studs available, just put it over
|
|
/*
|
|
BrickModel selectedBrickModel = PlayerPanel.Instance.selectedItem.item.brickModel;
|
|
Brick brick = Server.bricks[brickObj.name];
|
|
if (brick.model.type == selectedBrickModel.type) {
|
|
Vector3 pos = brickObj.transform.position;
|
|
pos.y += brick.model.heightInPlates * Server.plateHeight;
|
|
pos.y += 0.001f; // to make sure they don't collide, so we don't get a false isColliding=true
|
|
move(pos, Quaternion.identity);
|
|
return;
|
|
}*/
|
|
move(currentStud, rot);
|
|
}
|
|
|
|
public void move(Vector3 pos, Quaternion rotation) {
|
|
if (lastPos == pos) {
|
|
return;
|
|
}
|
|
pivot.position = pos;
|
|
pivot.rotation = rotation;
|
|
transform.localRotation = Quaternion.identity; // localy reset child rotation as it should always be identity
|
|
isColliding = false;
|
|
setValid(true);
|
|
lastPos = pos;
|
|
}
|
|
|
|
private void setValid (bool isValid) {
|
|
m.SetColor("_BaseColor", isValid ? Color.white : Color.red);
|
|
}
|
|
}
|
|
}
|