Compare commits
2 Commits
87cd7dbd0f
...
76c9679aa4
| Author | SHA1 | Date | |
|---|---|---|---|
| 76c9679aa4 | |||
| 50968843e5 |
@@ -228,6 +228,103 @@ PrefabInstance:
|
|||||||
insertIndex: -1
|
insertIndex: -1
|
||||||
addedObject: {fileID: 1929672450}
|
addedObject: {fileID: 1929672450}
|
||||||
m_SourcePrefab: {fileID: 3150474306388093854, guid: 1b4afb54fbf7b8c46908373caec5cfcf, type: 3}
|
m_SourcePrefab: {fileID: 3150474306388093854, guid: 1b4afb54fbf7b8c46908373caec5cfcf, type: 3}
|
||||||
|
<<<<<<< HEAD
|
||||||
|
--- !u!1 &153407402
|
||||||
|
GameObject:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
serializedVersion: 6
|
||||||
|
m_Component:
|
||||||
|
- component: {fileID: 153407404}
|
||||||
|
- component: {fileID: 153407403}
|
||||||
|
- component: {fileID: 153407405}
|
||||||
|
- component: {fileID: 153407406}
|
||||||
|
m_Layer: 0
|
||||||
|
m_Name: Player
|
||||||
|
m_TagString: Untagged
|
||||||
|
m_Icon: {fileID: 0}
|
||||||
|
m_NavMeshLayer: 0
|
||||||
|
m_StaticEditorFlags: 0
|
||||||
|
m_IsActive: 1
|
||||||
|
--- !u!143 &153407403
|
||||||
|
CharacterController:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 153407402}
|
||||||
|
m_Material: {fileID: 0}
|
||||||
|
m_IncludeLayers:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 0
|
||||||
|
m_ExcludeLayers:
|
||||||
|
serializedVersion: 2
|
||||||
|
m_Bits: 0
|
||||||
|
m_LayerOverridePriority: 0
|
||||||
|
m_ProvidesContacts: 0
|
||||||
|
m_Enabled: 1
|
||||||
|
serializedVersion: 3
|
||||||
|
m_Height: 2
|
||||||
|
m_Radius: 0.5
|
||||||
|
m_SlopeLimit: 45
|
||||||
|
m_StepOffset: 0.3
|
||||||
|
m_SkinWidth: 0.08
|
||||||
|
m_MinMoveDistance: 0.001
|
||||||
|
m_Center: {x: 0, y: 0, z: 0}
|
||||||
|
--- !u!4 &153407404
|
||||||
|
Transform:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 153407402}
|
||||||
|
serializedVersion: 2
|
||||||
|
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||||
|
m_LocalPosition: {x: 11.9, y: -9.03, z: 3.11}
|
||||||
|
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||||
|
m_ConstrainProportionsScale: 0
|
||||||
|
m_Children:
|
||||||
|
- {fileID: 2083332236}
|
||||||
|
m_Father: {fileID: 0}
|
||||||
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
|
--- !u!114 &153407405
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 153407402}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 6b87887b68361bb4dbb1327a9bb7aa82, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
walkSpeed: 8
|
||||||
|
runSpeed: 10
|
||||||
|
jumpHeight: 2
|
||||||
|
gravity: -9.81
|
||||||
|
mouseSensitivity: 2
|
||||||
|
maxLookAngle: 90
|
||||||
|
playerCamera: {fileID: 2083332238}
|
||||||
|
--- !u!114 &153407406
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 153407402}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 4a8ef389adf1c8b498e2e5d270f4ed94, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier:
|
||||||
|
crosshairColor: {r: 0.021564394, g: 0.9811321, b: 0, a: 1}
|
||||||
|
crosshairSize: 10
|
||||||
|
crosshairThickness: 2
|
||||||
|
=======
|
||||||
|
>>>>>>> 87cd7dbd0fb611b3cfcd19191dfcb36324bf72df
|
||||||
--- !u!1 &207645864 stripped
|
--- !u!1 &207645864 stripped
|
||||||
GameObject:
|
GameObject:
|
||||||
m_CorrespondingSourceObject: {fileID: 3579751623383085265, guid: 1b4afb54fbf7b8c46908373caec5cfcf, type: 3}
|
m_CorrespondingSourceObject: {fileID: 3579751623383085265, guid: 1b4afb54fbf7b8c46908373caec5cfcf, type: 3}
|
||||||
@@ -255,6 +352,52 @@ MeshCollider:
|
|||||||
m_Convex: 0
|
m_Convex: 0
|
||||||
m_CookingOptions: 30
|
m_CookingOptions: 30
|
||||||
m_Mesh: {fileID: -4926770370004109585, guid: 1b4afb54fbf7b8c46908373caec5cfcf, type: 3}
|
m_Mesh: {fileID: -4926770370004109585, guid: 1b4afb54fbf7b8c46908373caec5cfcf, type: 3}
|
||||||
|
--- !u!1 &431786309
|
||||||
|
GameObject:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
serializedVersion: 6
|
||||||
|
m_Component:
|
||||||
|
- component: {fileID: 431786311}
|
||||||
|
- component: {fileID: 431786310}
|
||||||
|
m_Layer: 0
|
||||||
|
m_Name: GameObject
|
||||||
|
m_TagString: Untagged
|
||||||
|
m_Icon: {fileID: 0}
|
||||||
|
m_NavMeshLayer: 0
|
||||||
|
m_StaticEditorFlags: 0
|
||||||
|
m_IsActive: 1
|
||||||
|
--- !u!114 &431786310
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 431786309}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 180c6606991a4b64f812f9713907fd0c, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: Assembly-CSharp::EnemySpawner
|
||||||
|
enemyType: 0
|
||||||
|
healthOverride: 0
|
||||||
|
--- !u!4 &431786311
|
||||||
|
Transform:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 431786309}
|
||||||
|
serializedVersion: 2
|
||||||
|
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||||
|
m_LocalPosition: {x: -6.61223, y: -16.71, z: 5.89}
|
||||||
|
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||||
|
m_ConstrainProportionsScale: 0
|
||||||
|
m_Children: []
|
||||||
|
m_Father: {fileID: 0}
|
||||||
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
--- !u!1 &705507993
|
--- !u!1 &705507993
|
||||||
GameObject:
|
GameObject:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -582,6 +725,52 @@ MeshCollider:
|
|||||||
m_Convex: 0
|
m_Convex: 0
|
||||||
m_CookingOptions: 30
|
m_CookingOptions: 30
|
||||||
m_Mesh: {fileID: 6626584385738647817, guid: 1b4afb54fbf7b8c46908373caec5cfcf, type: 3}
|
m_Mesh: {fileID: 6626584385738647817, guid: 1b4afb54fbf7b8c46908373caec5cfcf, type: 3}
|
||||||
|
--- !u!1 &1167301493
|
||||||
|
GameObject:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
serializedVersion: 6
|
||||||
|
m_Component:
|
||||||
|
- component: {fileID: 1167301495}
|
||||||
|
- component: {fileID: 1167301494}
|
||||||
|
m_Layer: 0
|
||||||
|
m_Name: GameObject (2)
|
||||||
|
m_TagString: Untagged
|
||||||
|
m_Icon: {fileID: 0}
|
||||||
|
m_NavMeshLayer: 0
|
||||||
|
m_StaticEditorFlags: 0
|
||||||
|
m_IsActive: 1
|
||||||
|
--- !u!114 &1167301494
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 1167301493}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 180c6606991a4b64f812f9713907fd0c, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: Assembly-CSharp::EnemySpawner
|
||||||
|
enemyType: 2
|
||||||
|
healthOverride: 0
|
||||||
|
--- !u!4 &1167301495
|
||||||
|
Transform:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 1167301493}
|
||||||
|
serializedVersion: 2
|
||||||
|
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||||
|
m_LocalPosition: {x: 14.85, y: -0, z: -2.86}
|
||||||
|
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||||
|
m_ConstrainProportionsScale: 0
|
||||||
|
m_Children: []
|
||||||
|
m_Father: {fileID: 0}
|
||||||
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
--- !u!1001 &1188990073
|
--- !u!1001 &1188990073
|
||||||
PrefabInstance:
|
PrefabInstance:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -766,6 +955,127 @@ MeshCollider:
|
|||||||
m_Convex: 1
|
m_Convex: 1
|
||||||
m_CookingOptions: 30
|
m_CookingOptions: 30
|
||||||
m_Mesh: {fileID: 0}
|
m_Mesh: {fileID: 0}
|
||||||
|
<<<<<<< HEAD
|
||||||
|
--- !u!1 &1908691865
|
||||||
|
GameObject:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
serializedVersion: 6
|
||||||
|
m_Component:
|
||||||
|
- component: {fileID: 1908691867}
|
||||||
|
- component: {fileID: 1908691866}
|
||||||
|
m_Layer: 0
|
||||||
|
m_Name: GameObject (1)
|
||||||
|
m_TagString: Untagged
|
||||||
|
m_Icon: {fileID: 0}
|
||||||
|
m_NavMeshLayer: 0
|
||||||
|
m_StaticEditorFlags: 0
|
||||||
|
m_IsActive: 1
|
||||||
|
--- !u!114 &1908691866
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 1908691865}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 180c6606991a4b64f812f9713907fd0c, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: Assembly-CSharp::EnemySpawner
|
||||||
|
enemyType: 1
|
||||||
|
healthOverride: 0
|
||||||
|
--- !u!4 &1908691867
|
||||||
|
Transform:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 1908691865}
|
||||||
|
serializedVersion: 2
|
||||||
|
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||||
|
m_LocalPosition: {x: 19.42, y: -0, z: 10.85739}
|
||||||
|
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||||
|
m_ConstrainProportionsScale: 0
|
||||||
|
m_Children: []
|
||||||
|
m_Father: {fileID: 0}
|
||||||
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
|
--- !u!1 &1914211071
|
||||||
|
GameObject:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
serializedVersion: 6
|
||||||
|
m_Component:
|
||||||
|
- component: {fileID: 1914211072}
|
||||||
|
- component: {fileID: 1914211073}
|
||||||
|
- component: {fileID: 1914211074}
|
||||||
|
m_Layer: 0
|
||||||
|
m_Name: Gun
|
||||||
|
m_TagString: Untagged
|
||||||
|
m_Icon: {fileID: 0}
|
||||||
|
m_NavMeshLayer: 0
|
||||||
|
m_StaticEditorFlags: 0
|
||||||
|
m_IsActive: 1
|
||||||
|
--- !u!4 &1914211072
|
||||||
|
Transform:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 1914211071}
|
||||||
|
serializedVersion: 2
|
||||||
|
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||||
|
m_LocalPosition: {x: 0.3, y: -0.2, z: 0.5}
|
||||||
|
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||||
|
m_ConstrainProportionsScale: 0
|
||||||
|
m_Children: []
|
||||||
|
m_Father: {fileID: 2083332236}
|
||||||
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
|
--- !u!114 &1914211073
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 1914211071}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: e348b4e172ba23d45960c0071cc09b1f, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: Assembly-CSharp::SimpleGun
|
||||||
|
damage: 25
|
||||||
|
range: 100
|
||||||
|
fireRate: 0.5
|
||||||
|
maxAmmo: 30
|
||||||
|
currentAmmo: 0
|
||||||
|
isAutomatic: 1
|
||||||
|
recoilKickback: 0.08
|
||||||
|
recoilKickUp: 0.04
|
||||||
|
recoilRecoverySpeed: 12
|
||||||
|
bobFrequency: 10
|
||||||
|
bobHorizontalAmplitude: 0.05
|
||||||
|
bobVerticalAmplitude: 0.03
|
||||||
|
sprintBobMultiplier: 1.5
|
||||||
|
bobReturnSpeed: 6
|
||||||
|
fpsCam: {fileID: 0}
|
||||||
|
--- !u!114 &1914211074
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 1914211071}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 9ad5c2eb6d6ecad47af3a531cd239ec5, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: Assembly-CSharp::CameraShake
|
||||||
|
=======
|
||||||
|
>>>>>>> 87cd7dbd0fb611b3cfcd19191dfcb36324bf72df
|
||||||
--- !u!1 &1929672446 stripped
|
--- !u!1 &1929672446 stripped
|
||||||
GameObject:
|
GameObject:
|
||||||
m_CorrespondingSourceObject: {fileID: -3210541856918053980, guid: 1b4afb54fbf7b8c46908373caec5cfcf, type: 3}
|
m_CorrespondingSourceObject: {fileID: -3210541856918053980, guid: 1b4afb54fbf7b8c46908373caec5cfcf, type: 3}
|
||||||
@@ -960,3 +1270,6 @@ SceneRoots:
|
|||||||
- {fileID: 2129963323}
|
- {fileID: 2129963323}
|
||||||
- {fileID: 754218171}
|
- {fileID: 754218171}
|
||||||
- {fileID: 58167676}
|
- {fileID: 58167676}
|
||||||
|
- {fileID: 431786311}
|
||||||
|
- {fileID: 1908691867}
|
||||||
|
- {fileID: 1167301495}
|
||||||
|
|||||||
53
Assets/Scripts/CameraShake.cs
Normal file
53
Assets/Scripts/CameraShake.cs
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
public class CameraShake : MonoBehaviour
|
||||||
|
{
|
||||||
|
public static CameraShake Instance { get; private set; }
|
||||||
|
|
||||||
|
private float shakeDuration = 0f;
|
||||||
|
private float shakeIntensity = 0f;
|
||||||
|
private float shakeFalloff = 1f;
|
||||||
|
|
||||||
|
private Vector3 originalLocalPos;
|
||||||
|
private bool shaking = false;
|
||||||
|
|
||||||
|
void Awake()
|
||||||
|
{
|
||||||
|
Instance = this;
|
||||||
|
originalLocalPos = transform.localPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LateUpdate()
|
||||||
|
{
|
||||||
|
if (!shaking) return;
|
||||||
|
|
||||||
|
if (shakeDuration > 0f)
|
||||||
|
{
|
||||||
|
Vector3 offset = Random.insideUnitSphere * shakeIntensity;
|
||||||
|
offset.z = 0f; // Keep shake on X/Y only
|
||||||
|
transform.localPosition = originalLocalPos + offset;
|
||||||
|
|
||||||
|
shakeDuration -= Time.deltaTime;
|
||||||
|
shakeIntensity = Mathf.Lerp(shakeIntensity, 0f, Time.deltaTime * shakeFalloff);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
shaking = false;
|
||||||
|
shakeDuration = 0f;
|
||||||
|
transform.localPosition = originalLocalPos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Trigger a camera shake.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="duration">How long to shake (seconds)</param>
|
||||||
|
/// <param name="intensity">How far to displace the camera</param>
|
||||||
|
public void Shake(float duration, float intensity)
|
||||||
|
{
|
||||||
|
shakeDuration = duration;
|
||||||
|
shakeIntensity = intensity;
|
||||||
|
shakeFalloff = intensity / duration;
|
||||||
|
shaking = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/Scripts/CameraShake.cs.meta
Normal file
2
Assets/Scripts/CameraShake.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9ad5c2eb6d6ecad47af3a531cd239ec5
|
||||||
93
Assets/Scripts/EnemyHealth.cs
Normal file
93
Assets/Scripts/EnemyHealth.cs
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
public class EnemyHealth : MonoBehaviour
|
||||||
|
{
|
||||||
|
public float maxHealth = 100f;
|
||||||
|
public float currentHealth;
|
||||||
|
|
||||||
|
[Header("Death Settings")]
|
||||||
|
public bool destroyOnDeath = true;
|
||||||
|
public float deathDelay = 0.1f;
|
||||||
|
|
||||||
|
private bool isDead = false;
|
||||||
|
|
||||||
|
void Start()
|
||||||
|
{
|
||||||
|
currentHealth = maxHealth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TakeDamage(float amount)
|
||||||
|
{
|
||||||
|
if (isDead) return;
|
||||||
|
|
||||||
|
currentHealth -= amount;
|
||||||
|
Debug.Log($"{gameObject.name} took {amount} damage! HP: {currentHealth}/{maxHealth}");
|
||||||
|
|
||||||
|
// Flash red on hit
|
||||||
|
StartCoroutine(HitFlash());
|
||||||
|
|
||||||
|
if (currentHealth <= 0f)
|
||||||
|
{
|
||||||
|
Die();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
System.Collections.IEnumerator HitFlash()
|
||||||
|
{
|
||||||
|
Renderer[] renderers = GetComponentsInChildren<Renderer>();
|
||||||
|
if (renderers.Length == 0) yield break;
|
||||||
|
|
||||||
|
// Store original colors
|
||||||
|
Color[] originalColors = new Color[renderers.Length];
|
||||||
|
for (int i = 0; i < renderers.Length; i++)
|
||||||
|
originalColors[i] = renderers[i].material.color;
|
||||||
|
|
||||||
|
// Flash all parts white-red
|
||||||
|
for (int i = 0; i < renderers.Length; i++)
|
||||||
|
renderers[i].material.color = new Color(1f, 0.3f, 0.3f);
|
||||||
|
|
||||||
|
yield return new WaitForSeconds(0.08f);
|
||||||
|
|
||||||
|
// Restore
|
||||||
|
if (!isDead)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < renderers.Length; i++)
|
||||||
|
{
|
||||||
|
if (renderers[i] != null)
|
||||||
|
renderers[i].material.color = originalColors[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Die()
|
||||||
|
{
|
||||||
|
isDead = true;
|
||||||
|
Debug.Log($"{gameObject.name} KILLED!");
|
||||||
|
|
||||||
|
if (destroyOnDeath)
|
||||||
|
{
|
||||||
|
// Quick death: disable collider immediately, destroy after delay
|
||||||
|
Collider col = GetComponent<Collider>();
|
||||||
|
if (col != null) col.enabled = false;
|
||||||
|
|
||||||
|
// Optional: add a little "pop" by scaling down
|
||||||
|
StartCoroutine(DeathEffect());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
System.Collections.IEnumerator DeathEffect()
|
||||||
|
{
|
||||||
|
float timer = 0f;
|
||||||
|
Vector3 startScale = transform.localScale;
|
||||||
|
|
||||||
|
while (timer < deathDelay)
|
||||||
|
{
|
||||||
|
timer += Time.deltaTime;
|
||||||
|
float t = timer / deathDelay;
|
||||||
|
transform.localScale = Vector3.Lerp(startScale, Vector3.zero, t);
|
||||||
|
yield return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Destroy(gameObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/Scripts/EnemyHealth.cs.meta
Normal file
2
Assets/Scripts/EnemyHealth.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 13343cfbf2e477741a831a3db3c9cf80
|
||||||
81
Assets/Scripts/EnemySpawner.cs
Normal file
81
Assets/Scripts/EnemySpawner.cs
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Drop this anywhere in your scene to spawn a humanoid enemy at that position.
|
||||||
|
/// Configure the enemy type in the Inspector then hit Play.
|
||||||
|
/// Delete this script after you've set up your enemies if you prefer manual placement.
|
||||||
|
/// </summary>
|
||||||
|
public class EnemySpawner : MonoBehaviour
|
||||||
|
{
|
||||||
|
public HumanoidEnemy.EnemyType enemyType = HumanoidEnemy.EnemyType.Grunt;
|
||||||
|
|
||||||
|
[Header("Health Override (0 = use defaults)")]
|
||||||
|
public float healthOverride = 0f;
|
||||||
|
|
||||||
|
void Start()
|
||||||
|
{
|
||||||
|
SpawnEnemy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpawnEnemy()
|
||||||
|
{
|
||||||
|
GameObject enemyObj = new GameObject($"Enemy_{enemyType}");
|
||||||
|
enemyObj.transform.position = transform.position;
|
||||||
|
enemyObj.transform.rotation = transform.rotation;
|
||||||
|
|
||||||
|
// Add CharacterController (required by HumanoidEnemy)
|
||||||
|
CharacterController cc = enemyObj.AddComponent<CharacterController>();
|
||||||
|
cc.slopeLimit = 45f;
|
||||||
|
cc.stepOffset = 0.5f;
|
||||||
|
|
||||||
|
// Add health
|
||||||
|
EnemyHealth health = enemyObj.AddComponent<EnemyHealth>();
|
||||||
|
|
||||||
|
// Add enemy AI + model
|
||||||
|
HumanoidEnemy enemy = enemyObj.AddComponent<HumanoidEnemy>();
|
||||||
|
enemy.enemyType = enemyType;
|
||||||
|
|
||||||
|
if (healthOverride > 0f)
|
||||||
|
{
|
||||||
|
health.maxHealth = healthOverride;
|
||||||
|
health.currentHealth = healthOverride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnDrawGizmos()
|
||||||
|
{
|
||||||
|
// Show enemy position in editor
|
||||||
|
switch (enemyType)
|
||||||
|
{
|
||||||
|
case HumanoidEnemy.EnemyType.Grunt:
|
||||||
|
Gizmos.color = new Color(0.8f, 0.5f, 0.2f, 0.8f);
|
||||||
|
DrawEnemyGizmo(1f);
|
||||||
|
break;
|
||||||
|
case HumanoidEnemy.EnemyType.Brute:
|
||||||
|
Gizmos.color = new Color(0.8f, 0.1f, 0.1f, 0.8f);
|
||||||
|
DrawEnemyGizmo(1.3f);
|
||||||
|
break;
|
||||||
|
case HumanoidEnemy.EnemyType.Runner:
|
||||||
|
Gizmos.color = new Color(0.2f, 0.7f, 0.2f, 0.8f);
|
||||||
|
DrawEnemyGizmo(0.9f);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawEnemyGizmo(float scale)
|
||||||
|
{
|
||||||
|
Vector3 pos = transform.position;
|
||||||
|
|
||||||
|
// Body
|
||||||
|
Gizmos.DrawCube(pos + Vector3.up * 1.15f * scale, new Vector3(0.45f, 0.6f, 0.25f) * scale);
|
||||||
|
// Head
|
||||||
|
Gizmos.DrawSphere(pos + Vector3.up * 1.65f * scale, 0.15f * scale);
|
||||||
|
// Legs
|
||||||
|
Gizmos.DrawCube(pos + Vector3.up * 0.4f * scale + Vector3.left * 0.12f, new Vector3(0.18f, 0.6f, 0.18f) * scale);
|
||||||
|
Gizmos.DrawCube(pos + Vector3.up * 0.4f * scale + Vector3.right * 0.12f, new Vector3(0.18f, 0.6f, 0.18f) * scale);
|
||||||
|
|
||||||
|
// Direction arrow
|
||||||
|
Gizmos.color = Color.yellow;
|
||||||
|
Gizmos.DrawRay(pos + Vector3.up * scale, transform.forward * 1.5f);
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/Scripts/EnemySpawner.cs.meta
Normal file
2
Assets/Scripts/EnemySpawner.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 180c6606991a4b64f812f9713907fd0c
|
||||||
@@ -3,8 +3,8 @@ using UnityEngine;
|
|||||||
public class FirstPersonController : MonoBehaviour
|
public class FirstPersonController : MonoBehaviour
|
||||||
{
|
{
|
||||||
[Header("Movement Settings")]
|
[Header("Movement Settings")]
|
||||||
public float walkSpeed = 8f;
|
public float walkSpeed = 50f; // Boosted from 8f to overcome collision issues
|
||||||
public float runSpeed = 14f;
|
public float runSpeed = 80f; // Boosted from 14f
|
||||||
public float jumpHeight = 2.5f;
|
public float jumpHeight = 2.5f;
|
||||||
public float gravity = -20f;
|
public float gravity = -20f;
|
||||||
|
|
||||||
@@ -23,66 +23,96 @@ public class FirstPersonController : MonoBehaviour
|
|||||||
|
|
||||||
void Start()
|
void Start()
|
||||||
{
|
{
|
||||||
Debug.Log( "Starting game" );
|
Debug.Log("Starting game");
|
||||||
// Get the CharacterController component
|
|
||||||
|
// FORCE NORMAL TIME (in case something external changed it)
|
||||||
|
Time.timeScale = 1f;
|
||||||
|
|
||||||
controller = GetComponent<CharacterController>();
|
controller = GetComponent<CharacterController>();
|
||||||
|
|
||||||
// If no camera is assigned, try to find one
|
if (controller == null)
|
||||||
if (playerCamera == null)
|
|
||||||
{
|
{
|
||||||
playerCamera = GetComponentInChildren<Camera>();
|
Debug.LogError("FirstPersonController: No CharacterController found!");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lock and hide the cursor
|
if (playerCamera == null)
|
||||||
|
playerCamera = GetComponentInChildren<Camera>();
|
||||||
|
|
||||||
Cursor.lockState = CursorLockMode.Locked;
|
Cursor.lockState = CursorLockMode.Locked;
|
||||||
Cursor.visible = false;
|
Cursor.visible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Update()
|
void Update()
|
||||||
{
|
{
|
||||||
// Check if player is on the ground
|
if (controller == null) return;
|
||||||
|
|
||||||
isGrounded = controller.isGrounded;
|
isGrounded = controller.isGrounded;
|
||||||
|
|
||||||
// Reset vertical velocity when grounded
|
|
||||||
if (isGrounded && velocity.y < 0)
|
if (isGrounded && velocity.y < 0)
|
||||||
{
|
|
||||||
velocity.y = -2f;
|
velocity.y = -2f;
|
||||||
|
|
||||||
|
// ─── Movement via direct KeyCode ───
|
||||||
|
float moveX = 0f;
|
||||||
|
float moveZ = 0f;
|
||||||
|
|
||||||
|
if (Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.UpArrow)) moveZ += 1f;
|
||||||
|
if (Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.DownArrow)) moveZ -= 1f;
|
||||||
|
if (Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.LeftArrow)) moveX -= 1f;
|
||||||
|
if (Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.RightArrow)) moveX += 1f;
|
||||||
|
|
||||||
|
// DEBUG: Log once when W is first pressed
|
||||||
|
if (Input.GetKeyDown(KeyCode.W))
|
||||||
|
{
|
||||||
|
Debug.Log($"[FPC DEBUG] W pressed | controller.enabled={controller.enabled} | position={transform.position} | isGrounded={isGrounded}");
|
||||||
|
Debug.Log($"[FPC DEBUG] Time.timeScale={Time.timeScale} | Time.deltaTime={Time.deltaTime} | walkSpeed={walkSpeed}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get movement input
|
|
||||||
float moveX = Input.GetAxis("Horizontal"); // A/D or Left/Right arrows
|
|
||||||
float moveZ = Input.GetAxis("Vertical"); // W/S or Up/Down arrows
|
|
||||||
|
|
||||||
// Calculate movement direction
|
|
||||||
Vector3 move = transform.right * moveX + transform.forward * moveZ;
|
Vector3 move = transform.right * moveX + transform.forward * moveZ;
|
||||||
|
|
||||||
// Determine current speed (run if Shift is held)
|
if (move.magnitude > 1f)
|
||||||
|
move.Normalize();
|
||||||
|
|
||||||
float currentSpeed = Input.GetKey(KeyCode.LeftShift) ? runSpeed : walkSpeed;
|
float currentSpeed = Input.GetKey(KeyCode.LeftShift) ? runSpeed : walkSpeed;
|
||||||
|
|
||||||
// Move the character
|
Vector3 posBefore = transform.position;
|
||||||
controller.Move(move * currentSpeed * Time.deltaTime);
|
controller.Move(move * currentSpeed * Time.deltaTime);
|
||||||
|
|
||||||
// Jumping
|
// DEBUG: Log if we tried to move but didn't
|
||||||
if (Input.GetButtonDown("Jump") && isGrounded)
|
if (move.magnitude > 0f && Input.GetKeyDown(KeyCode.W))
|
||||||
{
|
{
|
||||||
velocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity);
|
Vector3 posAfter = transform.position;
|
||||||
|
Debug.Log($"[FPC DEBUG] Move attempt: delta={move * currentSpeed * Time.deltaTime} | actualDelta={(posAfter - posBefore)} | controller.height={controller.height} | controller.radius={controller.radius}");
|
||||||
|
|
||||||
|
// Check what we're colliding with
|
||||||
|
Collider[] nearbyColliders = Physics.OverlapSphere(transform.position, controller.radius + 0.5f);
|
||||||
|
Debug.Log($"[FPC DEBUG] Found {nearbyColliders.Length} colliders near player");
|
||||||
|
foreach (Collider col in nearbyColliders)
|
||||||
|
{
|
||||||
|
if (col != controller && !(col is CharacterController))
|
||||||
|
Debug.LogWarning($"[FPC DEBUG] Nearby collider: {col.gameObject.name} on layer {LayerMask.LayerToName(col.gameObject.layer)}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply gravity
|
// Jumping
|
||||||
|
if (Input.GetKey(KeyCode.Space) && isGrounded)
|
||||||
|
velocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity);
|
||||||
|
|
||||||
|
// Gravity
|
||||||
velocity.y += gravity * Time.deltaTime;
|
velocity.y += gravity * Time.deltaTime;
|
||||||
controller.Move(velocity * Time.deltaTime);
|
controller.Move(velocity * Time.deltaTime);
|
||||||
|
|
||||||
// Mouse look
|
// Mouse look
|
||||||
HandleMouseLook();
|
HandleMouseLook();
|
||||||
|
|
||||||
// Press Escape to unlock cursor
|
// Escape to unlock cursor
|
||||||
if (Input.GetKeyDown(KeyCode.Escape))
|
if (Input.GetKeyDown(KeyCode.Escape))
|
||||||
{
|
{
|
||||||
Cursor.lockState = CursorLockMode.None;
|
Cursor.lockState = CursorLockMode.None;
|
||||||
Cursor.visible = true;
|
Cursor.visible = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Click to lock cursor again
|
// Click to re-lock cursor
|
||||||
if (Input.GetMouseButtonDown(0) && Cursor.lockState == CursorLockMode.None)
|
if (Input.GetMouseButtonDown(0) && Cursor.lockState == CursorLockMode.None)
|
||||||
{
|
{
|
||||||
Cursor.lockState = CursorLockMode.Locked;
|
Cursor.lockState = CursorLockMode.Locked;
|
||||||
@@ -92,16 +122,13 @@ public class FirstPersonController : MonoBehaviour
|
|||||||
|
|
||||||
void HandleMouseLook()
|
void HandleMouseLook()
|
||||||
{
|
{
|
||||||
// Get mouse input
|
|
||||||
float mouseX = Input.GetAxis("Mouse X") * mouseSensitivity;
|
float mouseX = Input.GetAxis("Mouse X") * mouseSensitivity;
|
||||||
float mouseY = Input.GetAxis("Mouse Y") * mouseSensitivity;
|
float mouseY = Input.GetAxis("Mouse Y") * mouseSensitivity;
|
||||||
|
|
||||||
// Rotate camera up/down (pitch)
|
|
||||||
xRotation -= mouseY;
|
xRotation -= mouseY;
|
||||||
xRotation = Mathf.Clamp(xRotation, -maxLookAngle, maxLookAngle);
|
xRotation = Mathf.Clamp(xRotation, -maxLookAngle, maxLookAngle);
|
||||||
playerCamera.transform.localRotation = Quaternion.Euler(xRotation, 0f, 0f);
|
playerCamera.transform.localRotation = Quaternion.Euler(xRotation, 0f, 0f);
|
||||||
|
|
||||||
// Rotate player left/right (yaw)
|
|
||||||
transform.Rotate(Vector3.up * mouseX);
|
transform.Rotate(Vector3.up * mouseX);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
394
Assets/Scripts/HumanoidEnemy.cs
Normal file
394
Assets/Scripts/HumanoidEnemy.cs
Normal file
@@ -0,0 +1,394 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using System.Collections;
|
||||||
|
|
||||||
|
[RequireComponent(typeof(CharacterController))]
|
||||||
|
public class HumanoidEnemy : MonoBehaviour
|
||||||
|
{
|
||||||
|
public enum EnemyType { Grunt, Brute, Runner }
|
||||||
|
|
||||||
|
[Header("Enemy Type")]
|
||||||
|
public EnemyType enemyType = EnemyType.Grunt;
|
||||||
|
|
||||||
|
[Header("Movement")]
|
||||||
|
public float moveSpeed = 5f;
|
||||||
|
public float chaseRange = 25f;
|
||||||
|
public float attackRange = 2f;
|
||||||
|
public float gravity = -20f;
|
||||||
|
|
||||||
|
[Header("Combat")]
|
||||||
|
public float attackDamage = 10f;
|
||||||
|
public float attackCooldown = 1f;
|
||||||
|
|
||||||
|
[Header("Patrol (optional)")]
|
||||||
|
public float patrolRadius = 8f;
|
||||||
|
public float patrolWaitTime = 2f;
|
||||||
|
|
||||||
|
// Internal
|
||||||
|
private Transform player;
|
||||||
|
private CharacterController controller;
|
||||||
|
private EnemyHealth health;
|
||||||
|
private Vector3 spawnPoint;
|
||||||
|
private Vector3 patrolTarget;
|
||||||
|
private float nextAttackTime;
|
||||||
|
private float verticalVelocity;
|
||||||
|
private float patrolWaitTimer;
|
||||||
|
private bool isAggro = false;
|
||||||
|
|
||||||
|
// Animation state
|
||||||
|
private Transform bodyRoot;
|
||||||
|
private Transform headTransform;
|
||||||
|
private Transform torsoTransform;
|
||||||
|
private Transform leftArm;
|
||||||
|
private Transform rightArm;
|
||||||
|
private Transform leftLeg;
|
||||||
|
private Transform rightLeg;
|
||||||
|
private float animTimer = 0f;
|
||||||
|
|
||||||
|
void Start()
|
||||||
|
{
|
||||||
|
controller = GetComponent<CharacterController>();
|
||||||
|
health = GetComponent<EnemyHealth>();
|
||||||
|
|
||||||
|
// Find player
|
||||||
|
GameObject playerObj = GameObject.Find("Player");
|
||||||
|
if (playerObj != null)
|
||||||
|
player = playerObj.transform;
|
||||||
|
|
||||||
|
spawnPoint = transform.position;
|
||||||
|
patrolTarget = GetRandomPatrolPoint();
|
||||||
|
|
||||||
|
ApplyTypeStats();
|
||||||
|
BuildHumanoidModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApplyTypeStats()
|
||||||
|
{
|
||||||
|
switch (enemyType)
|
||||||
|
{
|
||||||
|
case EnemyType.Grunt:
|
||||||
|
moveSpeed = 4f;
|
||||||
|
attackDamage = 10f;
|
||||||
|
attackCooldown = 1.2f;
|
||||||
|
chaseRange = 20f;
|
||||||
|
if (health != null) health.maxHealth = 80f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EnemyType.Brute:
|
||||||
|
moveSpeed = 2.5f;
|
||||||
|
attackDamage = 25f;
|
||||||
|
attackCooldown = 2f;
|
||||||
|
chaseRange = 18f;
|
||||||
|
if (health != null) health.maxHealth = 200f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EnemyType.Runner:
|
||||||
|
moveSpeed = 8f;
|
||||||
|
attackDamage = 8f;
|
||||||
|
attackCooldown = 0.6f;
|
||||||
|
chaseRange = 30f;
|
||||||
|
if (health != null) health.maxHealth = 50f;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (health != null)
|
||||||
|
health.currentHealth = health.maxHealth;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Update()
|
||||||
|
{
|
||||||
|
if (health != null && health.currentHealth <= 0f) return;
|
||||||
|
if (player == null) return;
|
||||||
|
|
||||||
|
float distToPlayer = Vector3.Distance(transform.position, player.position);
|
||||||
|
|
||||||
|
// Aggro check
|
||||||
|
if (distToPlayer <= chaseRange)
|
||||||
|
isAggro = true;
|
||||||
|
|
||||||
|
if (isAggro)
|
||||||
|
{
|
||||||
|
if (distToPlayer <= attackRange)
|
||||||
|
Attack();
|
||||||
|
else
|
||||||
|
ChasePlayer();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Patrol();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gravity
|
||||||
|
if (controller.isGrounded && verticalVelocity < 0f)
|
||||||
|
verticalVelocity = -2f;
|
||||||
|
verticalVelocity += gravity * Time.deltaTime;
|
||||||
|
controller.Move(Vector3.up * verticalVelocity * Time.deltaTime);
|
||||||
|
|
||||||
|
// Animate
|
||||||
|
AnimateModel(isAggro && distToPlayer > attackRange);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChasePlayer()
|
||||||
|
{
|
||||||
|
Vector3 direction = (player.position - transform.position);
|
||||||
|
direction.y = 0f;
|
||||||
|
direction.Normalize();
|
||||||
|
|
||||||
|
controller.Move(direction * moveSpeed * Time.deltaTime);
|
||||||
|
|
||||||
|
// Face the player
|
||||||
|
if (direction != Vector3.zero)
|
||||||
|
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(direction), Time.deltaTime * 8f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Patrol()
|
||||||
|
{
|
||||||
|
float distToTarget = Vector3.Distance(transform.position, patrolTarget);
|
||||||
|
|
||||||
|
if (distToTarget < 1.5f)
|
||||||
|
{
|
||||||
|
patrolWaitTimer += Time.deltaTime;
|
||||||
|
if (patrolWaitTimer >= patrolWaitTime)
|
||||||
|
{
|
||||||
|
patrolTarget = GetRandomPatrolPoint();
|
||||||
|
patrolWaitTimer = 0f;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 direction = (patrolTarget - transform.position);
|
||||||
|
direction.y = 0f;
|
||||||
|
direction.Normalize();
|
||||||
|
|
||||||
|
controller.Move(direction * (moveSpeed * 0.4f) * Time.deltaTime);
|
||||||
|
|
||||||
|
if (direction != Vector3.zero)
|
||||||
|
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(direction), Time.deltaTime * 4f);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3 GetRandomPatrolPoint()
|
||||||
|
{
|
||||||
|
Vector2 randomCircle = Random.insideUnitCircle * patrolRadius;
|
||||||
|
return spawnPoint + new Vector3(randomCircle.x, 0f, randomCircle.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Attack()
|
||||||
|
{
|
||||||
|
// Face the player
|
||||||
|
Vector3 direction = (player.position - transform.position);
|
||||||
|
direction.y = 0f;
|
||||||
|
if (direction != Vector3.zero)
|
||||||
|
transform.rotation = Quaternion.LookRotation(direction);
|
||||||
|
|
||||||
|
if (Time.time >= nextAttackTime)
|
||||||
|
{
|
||||||
|
nextAttackTime = Time.time + attackCooldown;
|
||||||
|
Debug.Log($"{gameObject.name} attacks for {attackDamage} damage!");
|
||||||
|
|
||||||
|
// Punch animation
|
||||||
|
StartCoroutine(PunchAnimation());
|
||||||
|
|
||||||
|
// TODO: Hook into player health system when you have one
|
||||||
|
// player.GetComponent<PlayerHealth>()?.TakeDamage(attackDamage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Procedural Humanoid Model ───
|
||||||
|
|
||||||
|
void BuildHumanoidModel()
|
||||||
|
{
|
||||||
|
// Sizing based on type
|
||||||
|
float scale = 1f;
|
||||||
|
Color skinColor = new Color(0.6f, 0.45f, 0.35f);
|
||||||
|
Color shirtColor;
|
||||||
|
Color pantsColor = new Color(0.2f, 0.2f, 0.25f);
|
||||||
|
|
||||||
|
switch (enemyType)
|
||||||
|
{
|
||||||
|
case EnemyType.Grunt:
|
||||||
|
scale = 1f;
|
||||||
|
shirtColor = new Color(0.4f, 0.25f, 0.2f); // brown shirt
|
||||||
|
break;
|
||||||
|
case EnemyType.Brute:
|
||||||
|
scale = 1.3f;
|
||||||
|
shirtColor = new Color(0.5f, 0.1f, 0.1f); // red shirt
|
||||||
|
skinColor = new Color(0.5f, 0.35f, 0.3f);
|
||||||
|
break;
|
||||||
|
case EnemyType.Runner:
|
||||||
|
scale = 0.9f;
|
||||||
|
shirtColor = new Color(0.2f, 0.35f, 0.2f); // green shirt
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
shirtColor = Color.gray;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
bodyRoot = new GameObject("BodyRoot").transform;
|
||||||
|
bodyRoot.SetParent(transform);
|
||||||
|
bodyRoot.localPosition = Vector3.zero;
|
||||||
|
bodyRoot.localRotation = Quaternion.identity;
|
||||||
|
|
||||||
|
// Head
|
||||||
|
headTransform = CreatePart("Head", bodyRoot,
|
||||||
|
new Vector3(0f, 1.65f, 0f) * scale,
|
||||||
|
new Vector3(0.3f, 0.3f, 0.3f) * scale,
|
||||||
|
skinColor, PrimitiveType.Sphere);
|
||||||
|
|
||||||
|
// Eyes (two small dark spheres)
|
||||||
|
CreatePart("LeftEye", headTransform,
|
||||||
|
new Vector3(-0.08f, 0.03f, 0.12f),
|
||||||
|
new Vector3(0.08f, 0.08f, 0.05f),
|
||||||
|
new Color(0.9f, 0.1f, 0.1f), PrimitiveType.Sphere);
|
||||||
|
|
||||||
|
CreatePart("RightEye", headTransform,
|
||||||
|
new Vector3(0.08f, 0.03f, 0.12f),
|
||||||
|
new Vector3(0.08f, 0.08f, 0.05f),
|
||||||
|
new Color(0.9f, 0.1f, 0.1f), PrimitiveType.Sphere);
|
||||||
|
|
||||||
|
// Torso
|
||||||
|
torsoTransform = CreatePart("Torso", bodyRoot,
|
||||||
|
new Vector3(0f, 1.15f, 0f) * scale,
|
||||||
|
new Vector3(0.45f, 0.6f, 0.25f) * scale,
|
||||||
|
shirtColor, PrimitiveType.Cube);
|
||||||
|
|
||||||
|
// Left Arm
|
||||||
|
leftArm = CreatePart("LeftArm", bodyRoot,
|
||||||
|
new Vector3(-0.35f, 1.15f, 0f) * scale,
|
||||||
|
new Vector3(0.15f, 0.55f, 0.15f) * scale,
|
||||||
|
skinColor, PrimitiveType.Cube);
|
||||||
|
|
||||||
|
// Right Arm
|
||||||
|
rightArm = CreatePart("RightArm", bodyRoot,
|
||||||
|
new Vector3(0.35f, 1.15f, 0f) * scale,
|
||||||
|
new Vector3(0.15f, 0.55f, 0.15f) * scale,
|
||||||
|
skinColor, PrimitiveType.Cube);
|
||||||
|
|
||||||
|
// Left Leg
|
||||||
|
leftLeg = CreatePart("LeftLeg", bodyRoot,
|
||||||
|
new Vector3(-0.12f, 0.4f, 0f) * scale,
|
||||||
|
new Vector3(0.18f, 0.6f, 0.18f) * scale,
|
||||||
|
pantsColor, PrimitiveType.Cube);
|
||||||
|
|
||||||
|
// Right Leg
|
||||||
|
rightLeg = CreatePart("RightLeg", bodyRoot,
|
||||||
|
new Vector3(0.12f, 0.4f, 0f) * scale,
|
||||||
|
new Vector3(0.18f, 0.6f, 0.18f) * scale,
|
||||||
|
pantsColor, PrimitiveType.Cube);
|
||||||
|
|
||||||
|
// Adjust CharacterController to fit
|
||||||
|
controller.height = 1.9f * scale;
|
||||||
|
controller.radius = 0.3f * scale;
|
||||||
|
controller.center = new Vector3(0f, 0.95f * scale, 0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
Transform CreatePart(string name, Transform parent, Vector3 localPos, Vector3 localScale, Color color, PrimitiveType shape = PrimitiveType.Cube)
|
||||||
|
{
|
||||||
|
GameObject part = GameObject.CreatePrimitive(shape);
|
||||||
|
part.name = name;
|
||||||
|
part.transform.SetParent(parent);
|
||||||
|
part.transform.localPosition = localPos;
|
||||||
|
part.transform.localScale = localScale;
|
||||||
|
part.transform.localRotation = Quaternion.identity;
|
||||||
|
|
||||||
|
// Remove collider from body parts (CharacterController handles collision)
|
||||||
|
Collider col = part.GetComponent<Collider>();
|
||||||
|
if (col != null) Destroy(col);
|
||||||
|
|
||||||
|
Renderer rend = part.GetComponent<Renderer>();
|
||||||
|
rend.material = new Material(Shader.Find("Standard"));
|
||||||
|
rend.material.color = color;
|
||||||
|
|
||||||
|
return part.transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Simple Limb Animation ───
|
||||||
|
|
||||||
|
void AnimateModel(bool isWalking)
|
||||||
|
{
|
||||||
|
if (bodyRoot == null) return;
|
||||||
|
|
||||||
|
if (isWalking)
|
||||||
|
{
|
||||||
|
animTimer += Time.deltaTime * moveSpeed * 1.5f;
|
||||||
|
|
||||||
|
float limbSwing = Mathf.Sin(animTimer) * 30f;
|
||||||
|
|
||||||
|
// Arms swing opposite to legs
|
||||||
|
if (leftArm != null)
|
||||||
|
leftArm.localRotation = Quaternion.Euler(limbSwing, 0f, 0f);
|
||||||
|
if (rightArm != null)
|
||||||
|
rightArm.localRotation = Quaternion.Euler(-limbSwing, 0f, 0f);
|
||||||
|
if (leftLeg != null)
|
||||||
|
leftLeg.localRotation = Quaternion.Euler(-limbSwing, 0f, 0f);
|
||||||
|
if (rightLeg != null)
|
||||||
|
rightLeg.localRotation = Quaternion.Euler(limbSwing, 0f, 0f);
|
||||||
|
|
||||||
|
// Slight torso bob
|
||||||
|
if (torsoTransform != null)
|
||||||
|
{
|
||||||
|
Vector3 torsoPos = torsoTransform.localPosition;
|
||||||
|
float scale = enemyType == EnemyType.Brute ? 1.3f : (enemyType == EnemyType.Runner ? 0.9f : 1f);
|
||||||
|
torsoPos.y = 1.15f * scale + Mathf.Abs(Mathf.Sin(animTimer * 2f)) * 0.03f;
|
||||||
|
torsoTransform.localPosition = torsoPos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Return to idle pose
|
||||||
|
animTimer = 0f;
|
||||||
|
if (leftArm != null)
|
||||||
|
leftArm.localRotation = Quaternion.Slerp(leftArm.localRotation, Quaternion.identity, Time.deltaTime * 5f);
|
||||||
|
if (rightArm != null)
|
||||||
|
rightArm.localRotation = Quaternion.Slerp(rightArm.localRotation, Quaternion.identity, Time.deltaTime * 5f);
|
||||||
|
if (leftLeg != null)
|
||||||
|
leftLeg.localRotation = Quaternion.Slerp(leftLeg.localRotation, Quaternion.identity, Time.deltaTime * 5f);
|
||||||
|
if (rightLeg != null)
|
||||||
|
rightLeg.localRotation = Quaternion.Slerp(rightLeg.localRotation, Quaternion.identity, Time.deltaTime * 5f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator PunchAnimation()
|
||||||
|
{
|
||||||
|
if (rightArm == null) yield break;
|
||||||
|
|
||||||
|
// Wind up
|
||||||
|
float timer = 0f;
|
||||||
|
float punchDuration = 0.15f;
|
||||||
|
|
||||||
|
while (timer < punchDuration)
|
||||||
|
{
|
||||||
|
timer += Time.deltaTime;
|
||||||
|
float t = timer / punchDuration;
|
||||||
|
rightArm.localRotation = Quaternion.Euler(Mathf.Lerp(0f, -90f, t), 0f, 0f);
|
||||||
|
yield return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Snap back
|
||||||
|
timer = 0f;
|
||||||
|
while (timer < punchDuration)
|
||||||
|
{
|
||||||
|
timer += Time.deltaTime;
|
||||||
|
float t = timer / punchDuration;
|
||||||
|
rightArm.localRotation = Quaternion.Euler(Mathf.Lerp(-90f, 0f, t), 0f, 0f);
|
||||||
|
yield return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
rightArm.localRotation = Quaternion.identity;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Gizmos ───
|
||||||
|
|
||||||
|
void OnDrawGizmosSelected()
|
||||||
|
{
|
||||||
|
// Chase range
|
||||||
|
Gizmos.color = new Color(1f, 0f, 0f, 0.15f);
|
||||||
|
Gizmos.DrawWireSphere(transform.position, chaseRange);
|
||||||
|
|
||||||
|
// Attack range
|
||||||
|
Gizmos.color = new Color(1f, 0.5f, 0f, 0.3f);
|
||||||
|
Gizmos.DrawWireSphere(transform.position, attackRange);
|
||||||
|
|
||||||
|
// Patrol radius
|
||||||
|
Gizmos.color = new Color(0f, 0.5f, 1f, 0.15f);
|
||||||
|
Gizmos.DrawWireSphere(Application.isPlaying ? spawnPoint : transform.position, patrolRadius);
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/Scripts/HumanoidEnemy.cs.meta
Normal file
2
Assets/Scripts/HumanoidEnemy.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 363b918c62c4e944faca34713da9120e
|
||||||
53
Assets/Scripts/RemoveDuplicateGuns.cs
Normal file
53
Assets/Scripts/RemoveDuplicateGuns.cs
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attach this to your Player object and hit Play once to remove duplicate guns.
|
||||||
|
/// Delete this script after running it.
|
||||||
|
/// </summary>
|
||||||
|
public class RemoveDuplicateGuns : MonoBehaviour
|
||||||
|
{
|
||||||
|
void Start()
|
||||||
|
{
|
||||||
|
Debug.Log("=== SEARCHING FOR DUPLICATE GUNS ===");
|
||||||
|
|
||||||
|
// Find all SimpleGun components in children
|
||||||
|
SimpleGun[] guns = GetComponentsInChildren<SimpleGun>();
|
||||||
|
|
||||||
|
Debug.Log($"Found {guns.Length} gun(s)");
|
||||||
|
|
||||||
|
if (guns.Length <= 1)
|
||||||
|
{
|
||||||
|
Debug.Log("Only one gun found - no duplicates to remove");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep the gun that's furthest from the player's feet (highest Y position)
|
||||||
|
SimpleGun highestGun = guns[0];
|
||||||
|
float highestY = guns[0].transform.position.y;
|
||||||
|
|
||||||
|
foreach (SimpleGun gun in guns)
|
||||||
|
{
|
||||||
|
Vector3 worldPos = gun.transform.position;
|
||||||
|
Debug.Log($"Gun at {gun.gameObject.name}: World Position = {worldPos}");
|
||||||
|
|
||||||
|
if (worldPos.y > highestY)
|
||||||
|
{
|
||||||
|
highestGun = gun;
|
||||||
|
highestY = worldPos.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove all guns except the highest one
|
||||||
|
foreach (SimpleGun gun in guns)
|
||||||
|
{
|
||||||
|
if (gun != highestGun)
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"REMOVING duplicate gun: {gun.gameObject.name} at {gun.transform.position}");
|
||||||
|
Destroy(gun.gameObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.Log($"Kept gun: {highestGun.gameObject.name} at {highestGun.transform.position}");
|
||||||
|
Debug.Log("=== DUPLICATE REMOVAL COMPLETE ===");
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/Scripts/RemoveDuplicateGuns.cs.meta
Normal file
2
Assets/Scripts/RemoveDuplicateGuns.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ed4b2209a2bb458458ceb5e8a3ba5950
|
||||||
@@ -5,81 +5,158 @@ public class SimpleGun : MonoBehaviour
|
|||||||
[Header("Gun Settings")]
|
[Header("Gun Settings")]
|
||||||
public float damage = 25f;
|
public float damage = 25f;
|
||||||
public float range = 100f;
|
public float range = 100f;
|
||||||
public float fireRate = 0.5f;
|
public float fireRate = 8f;
|
||||||
public int maxAmmo = 30;
|
public int maxAmmo = 30;
|
||||||
public int currentAmmo;
|
public int currentAmmo;
|
||||||
|
public bool isAutomatic = true;
|
||||||
|
|
||||||
|
[Header("Recoil Settings")]
|
||||||
|
public float recoilKickback = 0.08f;
|
||||||
|
public float recoilKickUp = 0.04f;
|
||||||
|
public float recoilRecoverySpeed = 12f;
|
||||||
|
|
||||||
|
[Header("Weapon Bob Settings")]
|
||||||
|
public float bobFrequency = 10f;
|
||||||
|
public float bobHorizontalAmplitude = 0.05f;
|
||||||
|
public float bobVerticalAmplitude = 0.03f;
|
||||||
|
public float sprintBobMultiplier = 1.5f;
|
||||||
|
public float bobReturnSpeed = 6f;
|
||||||
|
|
||||||
[Header("References")]
|
[Header("References")]
|
||||||
public Camera fpsCam;
|
public Camera fpsCam;
|
||||||
public ParticleSystem muzzleFlash;
|
|
||||||
|
|
||||||
|
// Muzzle flash
|
||||||
|
private GameObject muzzleFlashObj;
|
||||||
|
private Light muzzleLight;
|
||||||
|
private float muzzleFlashTimer = 0f;
|
||||||
|
private float muzzleFlashDuration = 0.05f;
|
||||||
|
|
||||||
|
// Impact
|
||||||
|
private static int maxDecals = 30;
|
||||||
|
private static GameObject[] decalPool;
|
||||||
|
private static int decalIndex = 0;
|
||||||
|
|
||||||
|
// Recoil
|
||||||
|
private Vector3 originalLocalPos;
|
||||||
|
private Vector3 recoilOffset;
|
||||||
|
|
||||||
|
// Bob
|
||||||
|
private float bobTimer = 0f;
|
||||||
|
private Vector3 bobOffset;
|
||||||
|
private CharacterController playerController;
|
||||||
|
|
||||||
|
// Fire timing
|
||||||
private float nextTimeToFire = 0f;
|
private float nextTimeToFire = 0f;
|
||||||
|
|
||||||
void Start()
|
void Start()
|
||||||
{
|
{
|
||||||
currentAmmo = maxAmmo;
|
currentAmmo = maxAmmo;
|
||||||
|
|
||||||
// Find camera if not assigned
|
|
||||||
if (fpsCam == null)
|
if (fpsCam == null)
|
||||||
{
|
|
||||||
fpsCam = Camera.main;
|
fpsCam = Camera.main;
|
||||||
|
if (fpsCam == null)
|
||||||
|
fpsCam = GetComponentInParent<Camera>();
|
||||||
|
|
||||||
|
playerController = GetComponentInParent<CharacterController>();
|
||||||
|
originalLocalPos = transform.localPosition;
|
||||||
|
|
||||||
|
// Strip colliders from all children so they don't block the CharacterController
|
||||||
|
StripChildColliders();
|
||||||
|
|
||||||
|
// NUCLEAR OPTION: Remove ANY collider on this object or children
|
||||||
|
Collider[] allColliders = GetComponentsInChildren<Collider>();
|
||||||
|
foreach (Collider col in allColliders)
|
||||||
|
{
|
||||||
|
if (!(col is CharacterController))
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"Found collider on {col.gameObject.name} - REMOVING IT!");
|
||||||
|
Destroy(col);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create simple gun visuals
|
// Only create a procedural gun if there's no existing model geometry
|
||||||
|
if (GetComponentsInChildren<MeshRenderer>().Length == 0)
|
||||||
CreateSimpleGunModel();
|
CreateSimpleGunModel();
|
||||||
|
|
||||||
|
CreateMuzzleFlash();
|
||||||
|
InitDecalPool();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StripChildColliders()
|
||||||
|
{
|
||||||
|
// Only strip colliders from direct children of the gun - never destroy CharacterControllers
|
||||||
|
foreach (Transform child in transform)
|
||||||
|
{
|
||||||
|
Collider col = child.GetComponent<Collider>();
|
||||||
|
if (col != null && !(col is CharacterController))
|
||||||
|
{
|
||||||
|
Destroy(col);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Update()
|
void Update()
|
||||||
{
|
{
|
||||||
// Shoot on left mouse button
|
// Shooting
|
||||||
if (Input.GetButton("Fire1") && Time.time >= nextTimeToFire)
|
if (isAutomatic ? Input.GetButton("Fire1") : Input.GetButtonDown("Fire1"))
|
||||||
|
{
|
||||||
|
if (Time.time >= nextTimeToFire)
|
||||||
{
|
{
|
||||||
nextTimeToFire = Time.time + 1f / fireRate;
|
nextTimeToFire = Time.time + 1f / fireRate;
|
||||||
Shoot();
|
Shoot();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Reload on R key
|
// Reload
|
||||||
if (Input.GetKeyDown(KeyCode.R))
|
if (Input.GetKeyDown(KeyCode.R))
|
||||||
{
|
|
||||||
Reload();
|
Reload();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CreateSimpleGunModel()
|
// Recover from recoil
|
||||||
|
recoilOffset = Vector3.Lerp(recoilOffset, Vector3.zero, Time.deltaTime * recoilRecoverySpeed);
|
||||||
|
|
||||||
|
// Weapon bob
|
||||||
|
UpdateBob();
|
||||||
|
|
||||||
|
// Apply combined position: original + recoil + bob
|
||||||
|
transform.localPosition = originalLocalPos + recoilOffset + bobOffset;
|
||||||
|
|
||||||
|
// Muzzle flash timer
|
||||||
|
if (muzzleFlashTimer > 0f)
|
||||||
{
|
{
|
||||||
// Main body
|
muzzleFlashTimer -= Time.deltaTime;
|
||||||
GameObject body = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
if (muzzleFlashTimer <= 0f)
|
||||||
body.transform.SetParent(transform);
|
{
|
||||||
body.transform.localPosition = new Vector3(0, -0.1f, 0.3f);
|
if (muzzleFlashObj != null) muzzleFlashObj.SetActive(false);
|
||||||
body.transform.localScale = new Vector3(0.1f, 0.15f, 0.4f);
|
if (muzzleLight != null) muzzleLight.enabled = false;
|
||||||
body.transform.localRotation = Quaternion.identity;
|
}
|
||||||
Destroy(body.GetComponent<Collider>()); // Remove collider
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Set color
|
void UpdateBob()
|
||||||
Renderer bodyRenderer = body.GetComponent<Renderer>();
|
{
|
||||||
bodyRenderer.material.color = new Color(0.2f, 0.2f, 0.2f); // Dark gray
|
if (playerController == null)
|
||||||
|
{
|
||||||
|
bobOffset = Vector3.zero;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Barrel
|
Vector3 horizontalVelocity = new Vector3(playerController.velocity.x, 0f, playerController.velocity.z);
|
||||||
GameObject barrel = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
|
bool isMoving = horizontalVelocity.magnitude > 0.5f && playerController.isGrounded;
|
||||||
barrel.transform.SetParent(transform);
|
|
||||||
barrel.transform.localPosition = new Vector3(0, -0.05f, 0.6f);
|
|
||||||
barrel.transform.localScale = new Vector3(0.03f, 0.15f, 0.03f);
|
|
||||||
barrel.transform.localRotation = Quaternion.Euler(90, 0, 0);
|
|
||||||
Destroy(barrel.GetComponent<Collider>());
|
|
||||||
|
|
||||||
Renderer barrelRenderer = barrel.GetComponent<Renderer>();
|
if (isMoving)
|
||||||
barrelRenderer.material.color = new Color(0.1f, 0.1f, 0.1f); // Even darker
|
{
|
||||||
|
float multiplier = Input.GetKey(KeyCode.LeftShift) ? sprintBobMultiplier : 1f;
|
||||||
|
bobTimer += Time.deltaTime * bobFrequency * multiplier;
|
||||||
|
|
||||||
// Handle
|
float bobX = Mathf.Sin(bobTimer) * bobHorizontalAmplitude * multiplier;
|
||||||
GameObject handle = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
float bobY = Mathf.Sin(bobTimer * 2f) * bobVerticalAmplitude * multiplier;
|
||||||
handle.transform.SetParent(transform);
|
bobOffset = new Vector3(bobX, bobY, 0f);
|
||||||
handle.transform.localPosition = new Vector3(0, -0.25f, 0.15f);
|
}
|
||||||
handle.transform.localScale = new Vector3(0.08f, 0.2f, 0.1f);
|
else
|
||||||
handle.transform.localRotation = Quaternion.Euler(15, 0, 0);
|
{
|
||||||
Destroy(handle.GetComponent<Collider>());
|
bobTimer = 0f;
|
||||||
|
bobOffset = Vector3.Lerp(bobOffset, Vector3.zero, Time.deltaTime * bobReturnSpeed);
|
||||||
Renderer handleRenderer = handle.GetComponent<Renderer>();
|
}
|
||||||
handleRenderer.material.color = new Color(0.3f, 0.2f, 0.1f); // Brown
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shoot()
|
void Shoot()
|
||||||
@@ -91,25 +168,239 @@ public class SimpleGun : MonoBehaviour
|
|||||||
}
|
}
|
||||||
|
|
||||||
currentAmmo--;
|
currentAmmo--;
|
||||||
Debug.Log($"Shot fired! Ammo: {currentAmmo}/{maxAmmo}");
|
|
||||||
|
|
||||||
// Play muzzle flash if assigned
|
// Muzzle flash
|
||||||
if (muzzleFlash != null)
|
ShowMuzzleFlash();
|
||||||
{
|
|
||||||
muzzleFlash.Play();
|
// Recoil kick
|
||||||
}
|
ApplyRecoil();
|
||||||
|
|
||||||
|
// Screen shake
|
||||||
|
if (CameraShake.Instance != null)
|
||||||
|
CameraShake.Instance.Shake(0.06f, 0.08f);
|
||||||
|
|
||||||
|
// Raycast
|
||||||
|
if (fpsCam == null) return;
|
||||||
|
|
||||||
// Raycast from camera center
|
|
||||||
RaycastHit hit;
|
RaycastHit hit;
|
||||||
if (Physics.Raycast(fpsCam.transform.position, fpsCam.transform.forward, out hit, range))
|
Vector3 origin = fpsCam.transform.position;
|
||||||
|
Vector3 direction = fpsCam.transform.forward;
|
||||||
|
|
||||||
|
if (Physics.Raycast(origin, direction, out hit, range))
|
||||||
{
|
{
|
||||||
Debug.Log($"Hit: {hit.transform.name}");
|
Debug.Log($"Hit: {hit.transform.name}");
|
||||||
|
SpawnImpactEffect(hit.point, hit.normal);
|
||||||
|
|
||||||
// You can add hit detection for enemies here later
|
EnemyHealth enemy = hit.transform.GetComponent<EnemyHealth>();
|
||||||
// Example: hit.transform.GetComponent<Enemy>()?.TakeDamage(damage);
|
if (enemy != null)
|
||||||
|
enemy.TakeDamage(damage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ApplyRecoil()
|
||||||
|
{
|
||||||
|
recoilOffset += new Vector3(
|
||||||
|
Random.Range(-0.005f, 0.005f),
|
||||||
|
Random.Range(0f, recoilKickUp),
|
||||||
|
-recoilKickback
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Shader Helper ───
|
||||||
|
|
||||||
|
Material CreateUnlitMaterial(Color color)
|
||||||
|
{
|
||||||
|
string[] shaderNames = {
|
||||||
|
"Unlit/Color",
|
||||||
|
"Universal Render Pipeline/Unlit",
|
||||||
|
"Hidden/InternalErrorShader"
|
||||||
|
};
|
||||||
|
|
||||||
|
Shader shader = null;
|
||||||
|
foreach (string name in shaderNames)
|
||||||
|
{
|
||||||
|
shader = Shader.Find(name);
|
||||||
|
if (shader != null) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shader == null)
|
||||||
|
return new Material(Shader.Find("Standard")) { color = color };
|
||||||
|
|
||||||
|
Material mat = new Material(shader);
|
||||||
|
mat.color = color;
|
||||||
|
return mat;
|
||||||
|
}
|
||||||
|
|
||||||
|
Material CreateParticleMaterial(Color color)
|
||||||
|
{
|
||||||
|
string[] shaderNames = {
|
||||||
|
"Particles/Standard Unlit",
|
||||||
|
"Universal Render Pipeline/Particles/Unlit",
|
||||||
|
"Unlit/Color",
|
||||||
|
"Hidden/InternalErrorShader"
|
||||||
|
};
|
||||||
|
|
||||||
|
Shader shader = null;
|
||||||
|
foreach (string name in shaderNames)
|
||||||
|
{
|
||||||
|
shader = Shader.Find(name);
|
||||||
|
if (shader != null) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Material mat = new Material(shader);
|
||||||
|
mat.color = color;
|
||||||
|
return mat;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Muzzle Flash ───
|
||||||
|
|
||||||
|
void CreateMuzzleFlash()
|
||||||
|
{
|
||||||
|
muzzleFlashObj = GameObject.CreatePrimitive(PrimitiveType.Quad);
|
||||||
|
muzzleFlashObj.name = "MuzzleFlash";
|
||||||
|
muzzleFlashObj.transform.SetParent(transform);
|
||||||
|
muzzleFlashObj.transform.localPosition = new Vector3(0f, -0.05f, 0.75f);
|
||||||
|
muzzleFlashObj.transform.localScale = new Vector3(0.15f, 0.15f, 0.15f);
|
||||||
|
Destroy(muzzleFlashObj.GetComponent<Collider>());
|
||||||
|
|
||||||
|
Renderer r = muzzleFlashObj.GetComponent<Renderer>();
|
||||||
|
r.material = CreateUnlitMaterial(new Color(1f, 0.85f, 0.3f));
|
||||||
|
|
||||||
|
GameObject lightObj = new GameObject("MuzzleLight");
|
||||||
|
lightObj.transform.SetParent(muzzleFlashObj.transform);
|
||||||
|
lightObj.transform.localPosition = Vector3.zero;
|
||||||
|
muzzleLight = lightObj.AddComponent<Light>();
|
||||||
|
muzzleLight.type = LightType.Point;
|
||||||
|
muzzleLight.color = new Color(1f, 0.8f, 0.3f);
|
||||||
|
muzzleLight.range = 8f;
|
||||||
|
muzzleLight.intensity = 3f;
|
||||||
|
muzzleLight.enabled = false;
|
||||||
|
|
||||||
|
muzzleFlashObj.SetActive(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShowMuzzleFlash()
|
||||||
|
{
|
||||||
|
if (muzzleFlashObj == null) return;
|
||||||
|
|
||||||
|
muzzleFlashObj.SetActive(true);
|
||||||
|
if (muzzleLight != null) muzzleLight.enabled = true;
|
||||||
|
muzzleFlashTimer = muzzleFlashDuration;
|
||||||
|
|
||||||
|
float angle = Random.Range(0f, 360f);
|
||||||
|
muzzleFlashObj.transform.localRotation = Quaternion.Euler(0f, 0f, angle);
|
||||||
|
float scale = Random.Range(0.1f, 0.2f);
|
||||||
|
muzzleFlashObj.transform.localScale = new Vector3(scale, scale, scale);
|
||||||
|
if (muzzleLight != null) muzzleLight.intensity = Random.Range(2f, 4f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Impact Effects ───
|
||||||
|
|
||||||
|
void InitDecalPool()
|
||||||
|
{
|
||||||
|
if (decalPool != null) return;
|
||||||
|
|
||||||
|
decalPool = new GameObject[maxDecals];
|
||||||
|
for (int i = 0; i < maxDecals; i++)
|
||||||
|
{
|
||||||
|
GameObject decal = GameObject.CreatePrimitive(PrimitiveType.Quad);
|
||||||
|
decal.name = "BulletDecal";
|
||||||
|
decal.transform.localScale = new Vector3(0.1f, 0.1f, 0.1f);
|
||||||
|
Destroy(decal.GetComponent<Collider>());
|
||||||
|
|
||||||
|
Renderer dr = decal.GetComponent<Renderer>();
|
||||||
|
dr.material = CreateUnlitMaterial(new Color(0.05f, 0.05f, 0.05f));
|
||||||
|
|
||||||
|
decal.SetActive(false);
|
||||||
|
decalPool[i] = decal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpawnImpactEffect(Vector3 position, Vector3 normal)
|
||||||
|
{
|
||||||
|
if (decalPool == null) return;
|
||||||
|
|
||||||
|
GameObject decal = decalPool[decalIndex % maxDecals];
|
||||||
|
decalIndex++;
|
||||||
|
|
||||||
|
decal.SetActive(true);
|
||||||
|
decal.transform.position = position + normal * 0.005f;
|
||||||
|
decal.transform.rotation = Quaternion.LookRotation(-normal);
|
||||||
|
float s = Random.Range(0.06f, 0.12f);
|
||||||
|
decal.transform.localScale = new Vector3(s, s, s);
|
||||||
|
|
||||||
|
SpawnSparks(position, normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpawnSparks(Vector3 position, Vector3 normal)
|
||||||
|
{
|
||||||
|
GameObject sparksObj = new GameObject("ImpactSparks");
|
||||||
|
sparksObj.transform.position = position;
|
||||||
|
sparksObj.transform.rotation = Quaternion.LookRotation(normal);
|
||||||
|
|
||||||
|
ParticleSystem ps = sparksObj.AddComponent<ParticleSystem>();
|
||||||
|
ps.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear);
|
||||||
|
|
||||||
|
var main = ps.main;
|
||||||
|
main.duration = 0.1f;
|
||||||
|
main.loop = false;
|
||||||
|
main.playOnAwake = false;
|
||||||
|
main.startLifetime = new ParticleSystem.MinMaxCurve(0.1f, 0.3f);
|
||||||
|
main.startSpeed = new ParticleSystem.MinMaxCurve(3f, 8f);
|
||||||
|
main.startSize = new ParticleSystem.MinMaxCurve(0.015f, 0.04f);
|
||||||
|
main.startColor = new Color(1f, 0.8f, 0.3f);
|
||||||
|
main.maxParticles = 12;
|
||||||
|
main.gravityModifier = 2f;
|
||||||
|
main.simulationSpace = ParticleSystemSimulationSpace.World;
|
||||||
|
|
||||||
|
var emission = ps.emission;
|
||||||
|
emission.enabled = true;
|
||||||
|
emission.SetBursts(new ParticleSystem.Burst[] {
|
||||||
|
new ParticleSystem.Burst(0f, 6, 12)
|
||||||
|
});
|
||||||
|
emission.rateOverTime = 0;
|
||||||
|
|
||||||
|
var shape = ps.shape;
|
||||||
|
shape.shapeType = ParticleSystemShapeType.Cone;
|
||||||
|
shape.angle = 35f;
|
||||||
|
shape.radius = 0.01f;
|
||||||
|
|
||||||
|
ParticleSystemRenderer psr = sparksObj.GetComponent<ParticleSystemRenderer>();
|
||||||
|
psr.material = CreateParticleMaterial(new Color(1f, 0.9f, 0.4f));
|
||||||
|
|
||||||
|
ps.Play();
|
||||||
|
Destroy(sparksObj, 0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Procedural Gun Model (only if no children exist) ───
|
||||||
|
|
||||||
|
void CreateSimpleGunModel()
|
||||||
|
{
|
||||||
|
GameObject body = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
||||||
|
body.transform.SetParent(transform);
|
||||||
|
body.transform.localPosition = new Vector3(0, -0.1f, 0.3f);
|
||||||
|
body.transform.localScale = new Vector3(0.1f, 0.15f, 0.4f);
|
||||||
|
body.transform.localRotation = Quaternion.identity;
|
||||||
|
Destroy(body.GetComponent<Collider>());
|
||||||
|
body.GetComponent<Renderer>().material.color = new Color(0.2f, 0.2f, 0.2f);
|
||||||
|
|
||||||
|
GameObject barrel = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
|
||||||
|
barrel.transform.SetParent(transform);
|
||||||
|
barrel.transform.localPosition = new Vector3(0, -0.05f, 0.6f);
|
||||||
|
barrel.transform.localScale = new Vector3(0.03f, 0.15f, 0.03f);
|
||||||
|
barrel.transform.localRotation = Quaternion.Euler(90, 0, 0);
|
||||||
|
Destroy(barrel.GetComponent<Collider>());
|
||||||
|
barrel.GetComponent<Renderer>().material.color = new Color(0.1f, 0.1f, 0.1f);
|
||||||
|
|
||||||
|
GameObject handle = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
||||||
|
handle.transform.SetParent(transform);
|
||||||
|
handle.transform.localPosition = new Vector3(0, -0.25f, 0.15f);
|
||||||
|
handle.transform.localScale = new Vector3(0.08f, 0.2f, 0.1f);
|
||||||
|
handle.transform.localRotation = Quaternion.Euler(15, 0, 0);
|
||||||
|
Destroy(handle.GetComponent<Collider>());
|
||||||
|
handle.GetComponent<Renderer>().material.color = new Color(0.3f, 0.2f, 0.1f);
|
||||||
|
}
|
||||||
|
|
||||||
void Reload()
|
void Reload()
|
||||||
{
|
{
|
||||||
Debug.Log("Reloading...");
|
Debug.Log("Reloading...");
|
||||||
@@ -118,10 +409,12 @@ public class SimpleGun : MonoBehaviour
|
|||||||
|
|
||||||
void OnGUI()
|
void OnGUI()
|
||||||
{
|
{
|
||||||
// Simple ammo counter in bottom-right
|
GUIStyle ammoStyle = new GUIStyle();
|
||||||
GUI.color = Color.white;
|
ammoStyle.fontSize = 28;
|
||||||
GUI.Label(new Rect(Screen.width - 120, Screen.height - 40, 100, 30),
|
ammoStyle.fontStyle = FontStyle.Bold;
|
||||||
$"Ammo: {currentAmmo}/{maxAmmo}",
|
ammoStyle.normal.textColor = currentAmmo > 5 ? Color.white : Color.red;
|
||||||
new GUIStyle() { fontSize = 20, normal = new GUIStyleState() { textColor = Color.white } });
|
|
||||||
|
string ammoText = $"{currentAmmo} / {maxAmmo}";
|
||||||
|
GUI.Label(new Rect(Screen.width - 180, Screen.height - 55, 160, 40), ammoText, ammoStyle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
54
Assets/Scripts/WeaponBob.cs
Normal file
54
Assets/Scripts/WeaponBob.cs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
public class WeaponBob : MonoBehaviour
|
||||||
|
{
|
||||||
|
[Header("Bob Settings")]
|
||||||
|
public float bobFrequency = 10f;
|
||||||
|
public float bobHorizontalAmplitude = 0.05f;
|
||||||
|
public float bobVerticalAmplitude = 0.03f;
|
||||||
|
|
||||||
|
[Header("Sprint Multiplier")]
|
||||||
|
public float sprintBobMultiplier = 1.5f;
|
||||||
|
|
||||||
|
[Header("Smoothing")]
|
||||||
|
public float returnSpeed = 6f;
|
||||||
|
|
||||||
|
private Vector3 originalLocalPos;
|
||||||
|
private float bobTimer = 0f;
|
||||||
|
private CharacterController controller;
|
||||||
|
|
||||||
|
void Start()
|
||||||
|
{
|
||||||
|
originalLocalPos = transform.localPosition;
|
||||||
|
controller = GetComponentInParent<CharacterController>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Update()
|
||||||
|
{
|
||||||
|
if (controller == null) return;
|
||||||
|
|
||||||
|
// Check if the player is moving on the ground
|
||||||
|
Vector3 horizontalVelocity = new Vector3(controller.velocity.x, 0f, controller.velocity.z);
|
||||||
|
bool isMoving = horizontalVelocity.magnitude > 0.5f && controller.isGrounded;
|
||||||
|
|
||||||
|
if (isMoving)
|
||||||
|
{
|
||||||
|
float multiplier = Input.GetKey(KeyCode.LeftShift) ? sprintBobMultiplier : 1f;
|
||||||
|
bobTimer += Time.deltaTime * bobFrequency * multiplier;
|
||||||
|
|
||||||
|
float bobX = Mathf.Sin(bobTimer) * bobHorizontalAmplitude * multiplier;
|
||||||
|
float bobY = Mathf.Sin(bobTimer * 2f) * bobVerticalAmplitude * multiplier;
|
||||||
|
|
||||||
|
transform.localPosition = originalLocalPos + new Vector3(bobX, bobY, 0f);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bobTimer = 0f;
|
||||||
|
transform.localPosition = Vector3.Lerp(
|
||||||
|
transform.localPosition,
|
||||||
|
originalLocalPos,
|
||||||
|
Time.deltaTime * returnSpeed
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/Scripts/WeaponBob.cs.meta
Normal file
2
Assets/Scripts/WeaponBob.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4b2ef1ad29b012344bd26f70130d3c2e
|
||||||
133
SETUP_GUIDE.md
133
SETUP_GUIDE.md
@@ -1,105 +1,46 @@
|
|||||||
# LiDAR FPS Game - Setup Guide
|
# OGG Shooting System - Setup Guide
|
||||||
|
|
||||||
## Project Structure Created:
|
## Scene Hierarchy (how it should look)
|
||||||
- **Assets/Scripts/** - Your C# scripts (including FirstPersonController.cs)
|
|
||||||
- **Assets/Models/LidarScans/** - Where you'll import your GLB files
|
|
||||||
- **Assets/Prefabs/** - For reusable game objects
|
|
||||||
- **Assets/Scenes/** - Your game levels
|
|
||||||
|
|
||||||
## Step 1: Import Your GLB Files
|
```
|
||||||
|
Player [CharacterController + FirstPersonController + SimpleCrosshair]
|
||||||
|
└── Camera [Camera + AudioListener + CameraShake + WeaponBob]
|
||||||
|
└── Gun [SimpleGun]
|
||||||
|
```
|
||||||
|
|
||||||
1. Open Unity and your OGG project
|
## Step-by-step Setup
|
||||||
2. Copy some GLB files from `K:\OGG\lidar\` to `K:\OGG\OGG\Assets\Models\LidarScans\`
|
|
||||||
- Start with one or two to test (e.g., "my bedroom.glb" or "studio old.glb")
|
|
||||||
3. Unity will automatically import them
|
|
||||||
|
|
||||||
## Step 2: Install glTFast Package (for better GLB support)
|
### 1. Player object (already done)
|
||||||
|
- Has: CharacterController, FirstPersonController, SimpleCrosshair ✔
|
||||||
|
- **Fix**: Drag the **Camera** child into the `Player Camera` field on FirstPersonController
|
||||||
|
|
||||||
1. In Unity, go to **Window → Package Manager**
|
### 2. Camera object (child of Player)
|
||||||
2. Click the **"+"** button in top-left
|
- Has: Camera, AudioListener ✔
|
||||||
3. Select **"Add package by name"**
|
- **Add Component**: `CameraShake`
|
||||||
4. Enter: `com.atteneder.gltfast`
|
- **Add Component**: `WeaponBob`
|
||||||
5. Click **Add**
|
|
||||||
|
|
||||||
## Step 3: Create Your Player
|
### 3. Gun object (child of Camera)
|
||||||
|
- Currently empty!
|
||||||
|
- **Add Component**: `SimpleGun`
|
||||||
|
- The gun auto-creates its own primitive model and muzzle flash
|
||||||
|
|
||||||
1. In the Hierarchy, **Right-click → Create Empty**
|
### 4. Testing enemies
|
||||||
2. Rename it to "Player"
|
- Create any GameObject (e.g. a Cube) in the scene
|
||||||
3. With Player selected, click **Add Component**
|
- Add a **Collider** (Box Collider, etc.)
|
||||||
4. Search for and add: **Character Controller**
|
- **Add Component**: `EnemyHealth`
|
||||||
5. Add Component again, search for: **First Person Controller** (our script)
|
- Shoot it and watch it flash red and pop!
|
||||||
|
|
||||||
## Step 4: Set Up the Camera
|
## Controls
|
||||||
|
| Key | Action |
|
||||||
|
|-----|--------|
|
||||||
|
| Left Click (hold) | Shoot (automatic fire) |
|
||||||
|
| R | Reload |
|
||||||
|
| W/A/S/D | Move |
|
||||||
|
| Shift | Sprint |
|
||||||
|
| Space | Jump |
|
||||||
|
|
||||||
1. In Hierarchy, **Right-click on Player → Camera**
|
## Tuning Tips
|
||||||
2. Position the camera:
|
- **Fire rate**: `SimpleGun.fireRate` (8 = fast, 2 = slow)
|
||||||
- Set Position Y to about 1.6 (eye height)
|
- **Shake intensity**: `CameraShake.Shake()` is called with (duration, intensity) - tweak in SimpleGun
|
||||||
- Reset X and Z to 0
|
- **Bob feel**: Adjust `WeaponBob.bobFrequency` and amplitude values
|
||||||
3. The FirstPersonController script should automatically find this camera
|
- **Recoil punch**: `SimpleGun.recoilKickback` and `recoilKickUp`
|
||||||
|
|
||||||
## Step 5: Import a LiDAR Scan into Your Scene
|
|
||||||
|
|
||||||
1. From Project window, navigate to **Assets/Models/LidarScans/**
|
|
||||||
2. Drag one of your GLB files into the Scene (Hierarchy)
|
|
||||||
3. Select the imported model in Hierarchy
|
|
||||||
4. In Inspector, click **Add Component → Mesh Collider**
|
|
||||||
- This allows the player to walk on it
|
|
||||||
5. You might need to adjust the scale:
|
|
||||||
- Try Scale: X=1, Y=1, Z=1 first
|
|
||||||
- If too big/small, adjust all values equally
|
|
||||||
|
|
||||||
## Step 6: Position Your Player
|
|
||||||
|
|
||||||
1. Select the Player in Hierarchy
|
|
||||||
2. In the Inspector, set Transform Position:
|
|
||||||
- Move the player above your scan (Y=2 or Y=3)
|
|
||||||
- Adjust X and Z to be inside the scan area
|
|
||||||
3. Make sure Character Controller settings are reasonable:
|
|
||||||
- Radius: 0.5
|
|
||||||
- Height: 2
|
|
||||||
- Center: Y=1
|
|
||||||
|
|
||||||
## Step 7: Test Your Game!
|
|
||||||
|
|
||||||
1. Click the **Play** button at the top
|
|
||||||
2. **Controls:**
|
|
||||||
- **WASD** or **Arrow Keys** - Move
|
|
||||||
- **Mouse** - Look around
|
|
||||||
- **Left Shift** - Run
|
|
||||||
- **Space** - Jump
|
|
||||||
- **Escape** - Unlock cursor
|
|
||||||
|
|
||||||
## Troubleshooting:
|
|
||||||
|
|
||||||
### Player falls through the floor:
|
|
||||||
- Make sure your LiDAR model has a **Mesh Collider** component
|
|
||||||
- Check that the Mesh Collider is enabled (checkbox ticked)
|
|
||||||
|
|
||||||
### Can't see anything:
|
|
||||||
- The camera might be inside geometry - move the Player position up (increase Y value)
|
|
||||||
- Check that the Camera is a child of Player and positioned at Y=1.6
|
|
||||||
|
|
||||||
### Movement feels weird:
|
|
||||||
- Adjust the Character Controller's Radius and Height
|
|
||||||
- Try different Walk/Run speeds in the FirstPersonController settings
|
|
||||||
|
|
||||||
### GLB files won't import:
|
|
||||||
- Install the glTFast package (see Step 2)
|
|
||||||
- Alternatively, you can convert GLB to OBJ using Blender if needed
|
|
||||||
|
|
||||||
## Next Steps:
|
|
||||||
|
|
||||||
Once you have basic movement working, we can add:
|
|
||||||
- Crosshair UI
|
|
||||||
- Health system
|
|
||||||
- Shooting mechanics
|
|
||||||
- Enemies/targets
|
|
||||||
- Multiple level loading
|
|
||||||
- Lighting improvements for your LiDAR scans
|
|
||||||
|
|
||||||
## Tips for LiDAR Scans:
|
|
||||||
|
|
||||||
- Your scans might be very detailed - this can slow down the game
|
|
||||||
- You may need to optimize them later (reduce polygon count)
|
|
||||||
- Start with smaller scans (single rooms) before loading larger areas
|
|
||||||
- You can have multiple scans in one scene to create larger levels
|
|
||||||
|
|||||||
Reference in New Issue
Block a user