Merge pull request #11 from JohnSWigner/ai_racer

add AI racer, race manager, checkpoint, finish line, respawn mechanis…
This commit is contained in:
JohnSWigner 2025-02-01 10:11:20 -05:00 committed by GitHub
commit a7afd1792e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
38 changed files with 8041 additions and 177 deletions

View File

@ -0,0 +1,246 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &5373302305645060479
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 403226101183159382}
- component: {fileID: 7605148307958671168}
- component: {fileID: 7040049591731627265}
m_Layer: 0
m_Name: Racer
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &403226101183159382
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5373302305645060479}
serializedVersion: 2
m_LocalRotation: {x: -0.7071068, y: -0, z: -0, w: 0.7071068}
m_LocalPosition: {x: 0, y: 0, z: -1.101}
m_LocalScale: {x: 50, y: 50, z: 50}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 713388573115023667}
m_LocalEulerAnglesHint: {x: -90, y: 90, z: -90}
--- !u!23 &7605148307958671168
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5373302305645060479}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: a8c192d1f2299e9429da6a6bbe7015b8, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!33 &7040049591731627265
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5373302305645060479}
m_Mesh: {fileID: -1971573099692935037, guid: 9b10eb187f9523b48b3484ebce59fa25, type: 3}
--- !u!1 &6468720760632047264
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 713388573115023667}
m_Layer: 0
m_Name: BankingObject
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &713388573115023667
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6468720760632047264}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 403226101183159382}
m_Father: {fileID: 2739604471974086380}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &9007727309090789879
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 2739604471974086380}
- component: {fileID: 7162886650759621711}
- component: {fileID: 6800183193855919764}
- component: {fileID: 4631925233965777590}
- component: {fileID: 8696968540172343143}
m_Layer: 0
m_Name: AIRacer
m_TagString: Racer
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &2739604471974086380
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 9007727309090789879}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 713388573115023667}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!54 &7162886650759621711
Rigidbody:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 9007727309090789879}
serializedVersion: 4
m_Mass: 1
m_Drag: 0
m_AngularDrag: 0.5
m_CenterOfMass: {x: 0, y: 0, z: 0}
m_InertiaTensor: {x: 1, y: 1, z: 1}
m_InertiaRotation: {x: 0, y: 0, z: 0, w: 1}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_ImplicitCom: 1
m_ImplicitTensor: 1
m_UseGravity: 1
m_IsKinematic: 0
m_Interpolate: 0
m_Constraints: 0
m_CollisionDetection: 0
--- !u!65 &6800183193855919764
BoxCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 9007727309090789879}
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_LayerOverridePriority: 0
m_IsTrigger: 0
m_ProvidesContacts: 0
m_Enabled: 1
serializedVersion: 3
m_Size: {x: 2, y: 0.75, z: 2.4}
m_Center: {x: 0, y: -0.1, z: -0.9}
--- !u!114 &4631925233965777590
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 9007727309090789879}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c250747ddb036f14faf25789ceb28a34, type: 3}
m_Name:
m_EditorClassIdentifier:
hoverHeight: 3
positionAdjustmentSpeed: 5
raycastDistance: 10
terrainLayer:
serializedVersion: 2
m_Bits: 0
movementSpeed: 50
acceleration: 5
deceleration: 7
rotationSpeed: 100
waypoints: []
waypointThreshold: 5
enableRubberBanding: 1
rubberBandingCoefficient: 0.1
maxSpeedMultiplier: 1.5
minSpeedMultiplier: 0.8
--- !u!114 &8696968540172343143
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 9007727309090789879}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 80f1410edd2d20447b553914aa3c9c7f, type: 3}
m_Name:
m_EditorClassIdentifier:
currentCheckpointIndex: 0
lapCount: 0

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 6651bdcbc08f8ed4384fe9587db0f16f
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -26,12 +26,12 @@ Transform:
m_PrefabAsset: {fileID: 0} m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5373302305645060479} m_GameObject: {fileID: 5373302305645060479}
serializedVersion: 2 serializedVersion: 2
m_LocalRotation: {x: -0.70710677, y: 0, z: 0, w: 0.70710677} m_LocalRotation: {x: -0.7071068, y: -0, z: -0, w: 0.7071068}
m_LocalPosition: {x: -0, y: 0, z: -1.101} m_LocalPosition: {x: 0, y: 0, z: -1.101}
m_LocalScale: {x: 50, y: 50, z: 50} m_LocalScale: {x: 50, y: 50, z: 50}
m_ConstrainProportionsScale: 0 m_ConstrainProportionsScale: 0
m_Children: [] m_Children: []
m_Father: {fileID: 2739604471974086380} m_Father: {fileID: 713388573115023667}
m_LocalEulerAnglesHint: {x: -90, y: 90, z: -90} m_LocalEulerAnglesHint: {x: -90, y: 90, z: -90}
--- !u!23 &7605148307958671168 --- !u!23 &7605148307958671168
MeshRenderer: MeshRenderer:
@ -86,6 +86,53 @@ MeshFilter:
m_PrefabAsset: {fileID: 0} m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5373302305645060479} m_GameObject: {fileID: 5373302305645060479}
m_Mesh: {fileID: -1971573099692935037, guid: 9b10eb187f9523b48b3484ebce59fa25, type: 3} m_Mesh: {fileID: -1971573099692935037, guid: 9b10eb187f9523b48b3484ebce59fa25, type: 3}
--- !u!1 &6468720760632047264
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 713388573115023667}
- component: {fileID: 6552287091957501518}
m_Layer: 0
m_Name: BankingObject
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &713388573115023667
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6468720760632047264}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 403226101183159382}
m_Father: {fileID: 2739604471974086380}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &6552287091957501518
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6468720760632047264}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: f060abec37829be48a8165282e7c3aeb, type: 3}
m_Name:
m_EditorClassIdentifier:
bankingSpeed: 5
maxBankingAngle: 30
--- !u!1 &9007727309090789879 --- !u!1 &9007727309090789879
GameObject: GameObject:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@ -97,11 +144,11 @@ GameObject:
- component: {fileID: 2739604471974086380} - component: {fileID: 2739604471974086380}
- component: {fileID: 7162886650759621711} - component: {fileID: 7162886650759621711}
- component: {fileID: 6800183193855919764} - component: {fileID: 6800183193855919764}
- component: {fileID: 2566772842932132389}
- component: {fileID: 341120459158792421} - component: {fileID: 341120459158792421}
- component: {fileID: 7105996084223619230}
m_Layer: 0 m_Layer: 0
m_Name: Player m_Name: Player
m_TagString: Untagged m_TagString: Racer
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
m_NavMeshLayer: 0 m_NavMeshLayer: 0
m_StaticEditorFlags: 0 m_StaticEditorFlags: 0
@ -119,7 +166,7 @@ Transform:
m_LocalScale: {x: 1, y: 1, z: 1} m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0 m_ConstrainProportionsScale: 0
m_Children: m_Children:
- {fileID: 403226101183159382} - {fileID: 713388573115023667}
m_Father: {fileID: 0} m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!54 &7162886650759621711 --- !u!54 &7162886650759621711
@ -170,21 +217,6 @@ BoxCollider:
serializedVersion: 3 serializedVersion: 3
m_Size: {x: 2, y: 0.75, z: 2.4} m_Size: {x: 2, y: 0.75, z: 2.4}
m_Center: {x: 0, y: -0.1, z: -0.9} m_Center: {x: 0, y: -0.1, z: -0.9}
--- !u!114 &2566772842932132389
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 9007727309090789879}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: de1851cd35e729f4aa33011a49cd59fb, type: 3}
m_Name:
m_EditorClassIdentifier:
TurnInputName: Horizontal
AccelerateButtonName: Accelerate
BrakeButtonName: Brake
--- !u!114 &341120459158792421 --- !u!114 &341120459158792421
MonoBehaviour: MonoBehaviour:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@ -207,3 +239,17 @@ MonoBehaviour:
acceleration: 3 acceleration: 3
deceleration: 2 deceleration: 2
rotationSpeed: 125 rotationSpeed: 125
--- !u!114 &7105996084223619230
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 9007727309090789879}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 80f1410edd2d20447b553914aa3c9c7f, type: 3}
m_Name:
m_EditorClassIdentifier:
currentCheckpointIndex: 0
lapCount: 0

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1e2afd01cab1aec41b5867293731f0b8
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,69 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &4611779275587207245
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 170758284542595608}
- component: {fileID: 312733210285042010}
- component: {fileID: 6172101860559313244}
m_Layer: 0
m_Name: Checkpoint
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &170758284542595608
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4611779275587207245}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: -0.0013773441, y: -0.0979718, z: -0.8798604}
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!65 &312733210285042010
BoxCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4611779275587207245}
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_LayerOverridePriority: 0
m_IsTrigger: 1
m_ProvidesContacts: 0
m_Enabled: 1
serializedVersion: 3
m_Size: {x: 50, y: 40, z: 1}
m_Center: {x: 0, y: 0, z: 0}
--- !u!114 &6172101860559313244
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4611779275587207245}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 7c3aaa50cd9e11d49801b7daff0a21bf, type: 3}
m_Name:
m_EditorClassIdentifier:
checkpointIndex: 0

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 42174ef287c46c342845e8be9f640353
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,68 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &4611779275587207245
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 170758284542595608}
- component: {fileID: 312733210285042010}
- component: {fileID: 5198925182810260566}
m_Layer: 0
m_Name: Finish Line
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &170758284542595608
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4611779275587207245}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: -0.0013773441, y: -0.0979718, z: -0.8798604}
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!65 &312733210285042010
BoxCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4611779275587207245}
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_LayerOverridePriority: 0
m_IsTrigger: 1
m_ProvidesContacts: 0
m_Enabled: 1
serializedVersion: 3
m_Size: {x: 50, y: 20, z: 1}
m_Center: {x: 0, y: 0, z: 0}
--- !u!114 &5198925182810260566
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4611779275587207245}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 9a4bc101ca357064ca138761e1319354, type: 3}
m_Name:
m_EditorClassIdentifier:

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: b44d494d42ce6164dbaa9b9c298b9e1d
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,51 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &2884735936553393805
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 6446736146779979254}
- component: {fileID: 8956791611276861029}
m_Layer: 0
m_Name: RaceManager
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &6446736146779979254
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2884735936553393805}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: -0.0013773441, y: -0.0979718, z: -0.8798604}
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!114 &8956791611276861029
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2884735936553393805}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 18ac36268412d1946a31d8945cc6ac33, type: 3}
m_Name:
m_EditorClassIdentifier:
totalLaps: 3
numberOfCheckpoints: 0
finishLine: {fileID: 0}
raceFinished: 0
winnerName:

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 3d3c243c1b15b8d47b49111dee600ae6
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

8
Assets/Resources.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a2203474888d52347830cc8c8f51e421
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

6866
Assets/Scenes/AITrack.unity Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 79f34c11850512b418ac4060394f87ce
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1232,7 +1232,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 39e00ff0228389448a3e4fa61b925b10, type: 3} m_Script: {fileID: 11500000, guid: 39e00ff0228389448a3e4fa61b925b10, type: 3}
m_Name: m_Name:
m_EditorClassIdentifier: m_EditorClassIdentifier:
scene_to_load: TestTrack scene_to_load: AITrack
controlsPanel: {fileID: 1583355326} controlsPanel: {fileID: 1583355326}
--- !u!1 &1397774624 --- !u!1 &1397774624
GameObject: GameObject:

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 933d22bf6aedba544849d3821a5d3a91 guid: 856adccddca4ccf42ae7e35ef8ad938e
DefaultImporter: DefaultImporter:
externalObjects: {} externalObjects: {}
userData: userData:

View File

@ -0,0 +1,322 @@
using UnityEngine;
public class AIHovercarController : MonoBehaviour
{
[Header("Hover Settings")]
public float hoverHeight = 3.0f;
public float positionAdjustmentSpeed = 10.0f;
public float raycastDistance = 10.0f;
public LayerMask terrainLayer;
[Header("Movement Settings")]
public float movementSpeed = 10.0f; // Max speed
public float acceleration = 5.0f; // How quickly to accelerate
public float deceleration = 7.0f; // How quickly to decelerate
public float rotationSpeed = 100.0f;
[Header("Checkpoint Navigation")]
[Tooltip("Ordered list of checkpoints for the AI to follow.")]
public Transform[] checkpoints;
[Tooltip("Distance from a checkpoint at which the AI switches to the next one.")]
public float checkpointThreshold = 5.0f;
private int currentCheckpointIndex = 0;
[Header("Random Steering Settings")]
[Tooltip("Probability per second that a random steering offset is applied (0 = never, 1 = every second on average).")]
public float randomSteeringProbability = 0.2f;
[Tooltip("Maximum random steering offset added (in normalized input units; e.g., 0.2 means up to ±20% extra steering).")]
public float randomSteeringMaxOffset = 0.2f;
[Header("Slowdown Settings")]
[Tooltip("Distance before a checkpoint at which the AI begins to slow down.")]
public float slowdownDistance = 10f;
[Tooltip("Strength of the slowdown (0 = no slowdown, 1 = full slowdown to minimum throttle at the checkpoint).")]
public float slowdownStrength = 0.5f;
[Header("Reset Settings")]
[Tooltip("Time (in seconds) without track contact before resetting the vehicle.")]
public float timeBeforeReset = 3.0f;
[Header("Track Orientation Settings")]
[Tooltip("Distance to search downward for track (used to align the vehicles bottom).")]
public float trackRaycastDistance = 10.0f;
[Tooltip("Tag used on the track geometry.")]
public string trackTag = "Track";
[Header("Avoidance Settings")]
[Tooltip("Radius within which the AI will try to avoid other racers.")]
public float avoidanceRadius = 5.0f;
[Tooltip("Strength of the steering adjustment to avoid collisions.")]
public float avoidanceSteeringStrength = 0.5f;
[Tooltip("Throttle multiplier applied when avoiding other racers (0 to 1, where lower means more slowdown).")]
public float avoidanceSlowdownMultiplier = 0.5f;
[Tooltip("Tag used to identify other racers for avoidance.")]
public string racerTag = "Racer";
private float currentSpeed = 0.0f;
private Rigidbody rb;
// Timer for lost ground/track contact
private float noContactTimer = 0.0f;
// Flag indicating if the hover raycast hit the terrain/track this physics frame
private bool groundContact = false;
void Start()
{
rb = GetComponent<Rigidbody>();
rb.useGravity = false;
}
void FixedUpdate()
{
HandleHovering();
HandleMovement();
// If no ground/track contact is detected, count up the timer.
if (!groundContact)
{
noContactTimer += Time.fixedDeltaTime;
if (noContactTimer >= timeBeforeReset)
{
ResetToCheckpoint();
noContactTimer = 0.0f; // Reset timer after repositioning.
}
}
else
{
// Reset the timer when ground/track contact is re-established.
noContactTimer = 0.0f;
}
}
/// <summary>
/// Hover logic: cast a ray downward, interpolate the hit point and normal on the terrain,
/// and adjust the vehicle's position and rotation so it hovers along the track.
/// Also sets the groundContact flag for reset timing.
/// </summary>
void HandleHovering()
{
RaycastHit hit;
Vector3 rayOrigin = transform.position;
if (Physics.Raycast(rayOrigin, -transform.up, out hit, raycastDistance, terrainLayer))
{
groundContact = true;
Mesh mesh = hit.collider.GetComponent<MeshFilter>().mesh;
int triangleIndex = hit.triangleIndex;
int vertex1Index = mesh.triangles[triangleIndex * 3 + 0];
int vertex2Index = mesh.triangles[triangleIndex * 3 + 1];
int vertex3Index = mesh.triangles[triangleIndex * 3 + 2];
Vector3 worldVertex1 = hit.collider.transform.TransformPoint(mesh.vertices[vertex1Index]);
Vector3 worldVertex2 = hit.collider.transform.TransformPoint(mesh.vertices[vertex2Index]);
Vector3 worldVertex3 = hit.collider.transform.TransformPoint(mesh.vertices[vertex3Index]);
Vector3 interpolatedPoint = worldVertex1 * hit.barycentricCoordinate.x +
worldVertex2 * hit.barycentricCoordinate.y +
worldVertex3 * hit.barycentricCoordinate.z;
Vector3 localNormal1 = mesh.normals[vertex1Index];
Vector3 localNormal2 = mesh.normals[vertex2Index];
Vector3 localNormal3 = mesh.normals[vertex3Index];
Vector3 worldNormal1 = hit.collider.transform.TransformDirection(localNormal1);
Vector3 worldNormal2 = hit.collider.transform.TransformDirection(localNormal2);
Vector3 worldNormal3 = hit.collider.transform.TransformDirection(localNormal3);
Vector3 interpolatedNormal = worldNormal1 * hit.barycentricCoordinate.x +
worldNormal2 * hit.barycentricCoordinate.y +
worldNormal3 * hit.barycentricCoordinate.z;
interpolatedNormal.Normalize();
Vector3 targetPosition = interpolatedPoint + interpolatedNormal * hoverHeight;
transform.position = Vector3.Lerp(transform.position, targetPosition, Time.fixedDeltaTime * positionAdjustmentSpeed);
Quaternion targetRotation = Quaternion.FromToRotation(transform.up, interpolatedNormal) * transform.rotation;
rb.rotation = Quaternion.Slerp(rb.rotation, targetRotation, Time.fixedDeltaTime * 5.0f);
}
else
{
groundContact = false;
}
}
/// <summary>
/// Simulates input by computing throttle and steering based on the AI's orientation relative to the next checkpoint.
/// New additions:
/// - Random steering offset.
/// - Throttle reduction when turning sharply and when approaching a checkpoint.
/// - Avoidance: steer away from and slow down for nearby racers.
/// </summary>
void HandleMovement()
{
// Default simulated inputs.
float verticalInput = 1f; // full throttle by default
float horizontalInput = 0f;
if (checkpoints != null && checkpoints.Length > 0)
{
Transform targetCheckpoint = checkpoints[currentCheckpointIndex];
Vector3 toCheckpoint = targetCheckpoint.position - transform.position;
float distanceToCheckpoint = toCheckpoint.magnitude;
Vector3 toCheckpointNormalized = toCheckpoint.normalized;
// If the target checkpoint is behind the vehicle, switch to the next one immediately.
if (Vector3.Dot(transform.forward, toCheckpointNormalized) < 0)
{
currentCheckpointIndex = (currentCheckpointIndex + 1) % checkpoints.Length;
}
else
{
// Compute steering input based on the angle between the vehicle's forward and the direction to the checkpoint.
Vector3 projectedForward = Vector3.ProjectOnPlane(transform.forward, transform.up).normalized;
Vector3 projectedTarget = Vector3.ProjectOnPlane(toCheckpointNormalized, transform.up).normalized;
float angle = Vector3.SignedAngle(projectedForward, projectedTarget, transform.up);
horizontalInput = Mathf.Clamp(angle / 45f, -1f, 1f);
// Add a random steering offset occasionally.
if (Random.value < randomSteeringProbability * Time.fixedDeltaTime)
{
float randomOffset = Random.Range(-randomSteeringMaxOffset, randomSteeringMaxOffset);
horizontalInput += randomOffset;
horizontalInput = Mathf.Clamp(horizontalInput, -1f, 1f);
}
// Reduce throttle when turning sharply.
verticalInput = Mathf.Clamp(1f - (Mathf.Abs(horizontalInput) * 0.5f), 0f, 1f);
// Additional slowdown when approaching a checkpoint.
if (distanceToCheckpoint < slowdownDistance)
{
float slowdownFactor = Mathf.Lerp(1 - slowdownStrength, 1f, distanceToCheckpoint / slowdownDistance);
verticalInput *= slowdownFactor;
}
// Switch to the next checkpoint when close enough.
if (distanceToCheckpoint < checkpointThreshold)
{
currentCheckpointIndex = (currentCheckpointIndex + 1) % checkpoints.Length;
}
}
}
// --- Avoidance Logic ---
// Look for nearby racers and, if any are detected, adjust steering and throttle.
Collider[] nearbyColliders = Physics.OverlapSphere(transform.position, avoidanceRadius);
Vector3 avoidanceVector = Vector3.zero;
int avoidanceCount = 0;
foreach (Collider col in nearbyColliders)
{
if (col.gameObject != gameObject && col.CompareTag(racerTag))
{
Vector3 diff = transform.position - col.transform.position;
if (diff.magnitude > 0)
{
// Closer objects contribute more.
avoidanceVector += diff.normalized / diff.magnitude;
avoidanceCount++;
}
}
}
if (avoidanceCount > 0)
{
avoidanceVector /= avoidanceCount;
// Project onto the horizontal plane.
avoidanceVector = Vector3.ProjectOnPlane(avoidanceVector, transform.up).normalized;
// Determine steering adjustment from avoidance.
float avoidanceAngle = Vector3.SignedAngle(transform.forward, avoidanceVector, transform.up);
float avoidanceInput = Mathf.Clamp(avoidanceAngle / 45f, -1f, 1f) * avoidanceSteeringStrength;
horizontalInput += avoidanceInput;
horizontalInput = Mathf.Clamp(horizontalInput, -1f, 1f);
// Slow down throttle when avoiding.
verticalInput *= avoidanceSlowdownMultiplier;
}
// --- End Avoidance Logic ---
// Apply movement: accelerate/decelerate toward the target speed, then update velocity and angular velocity.
float targetSpeed = verticalInput * movementSpeed;
if (verticalInput != 0)
{
currentSpeed = Mathf.Lerp(currentSpeed, targetSpeed, Time.fixedDeltaTime * acceleration);
}
else
{
currentSpeed = Mathf.Lerp(currentSpeed, 0, Time.fixedDeltaTime * deceleration);
}
rb.linearVelocity = transform.forward * currentSpeed;
float turn = horizontalInput * rotationSpeed;
rb.angularVelocity = transform.up * turn * Mathf.Deg2Rad;
}
/// <summary>
/// Resets the AI's position to the last checkpoint it passed and reorients it so that:
/// - Its front faces toward the nearest (other) checkpoint.
/// - Its bottom is aligned with the nearby track surface (determined via a raycast and track tag).
/// </summary>
void ResetToCheckpoint()
{
if (checkpoints == null || checkpoints.Length == 0)
{
Debug.LogWarning("No checkpoints assigned in the Inspector.");
return;
}
// Determine the last checkpoint passed.
int lastCheckpointIndex = currentCheckpointIndex - 1;
if (lastCheckpointIndex < 0)
{
lastCheckpointIndex = checkpoints.Length - 1;
}
Transform lastCheckpoint = checkpoints[lastCheckpointIndex];
// Find the nearest other checkpoint (to set the forward direction).
Transform nearestOther = null;
float nearestDistance = Mathf.Infinity;
foreach (Transform cp in checkpoints)
{
if (cp == lastCheckpoint)
continue;
float d = Vector3.Distance(lastCheckpoint.position, cp.position);
if (d < nearestDistance)
{
nearestDistance = d;
nearestOther = cp;
}
}
// Determine desired forward direction.
Vector3 desiredForward = (nearestOther != null)
? (nearestOther.position - lastCheckpoint.position).normalized
: transform.forward;
// Determine the tracks surface normal to align the vehicles bottom.
// Raycast downward from a point just above the checkpoint.
Vector3 desiredUp = Vector3.up; // Fallback
RaycastHit hit;
Vector3 rayOrigin = lastCheckpoint.position + Vector3.up * 1.0f;
if (Physics.Raycast(rayOrigin, -Vector3.up, out hit, trackRaycastDistance))
{
if (hit.collider.CompareTag(trackTag))
{
// Use the inverted hit normal so the vehicles bottom (transform.up) is flush with the track.
desiredUp = -hit.normal;
}
}
// Adjust desired forward so that it is perpendicular to desired up.
desiredForward = Vector3.ProjectOnPlane(desiredForward, desiredUp).normalized;
Quaternion desiredRotation = Quaternion.LookRotation(desiredForward, desiredUp);
// Reset position and orientation.
transform.position = lastCheckpoint.position;
transform.rotation = desiredRotation;
// Clear any existing motion.
rb.linearVelocity = Vector3.zero;
rb.angularVelocity = Vector3.zero;
Debug.Log("AI reset to checkpoint: " + lastCheckpoint.name +
(nearestOther != null ? " with front facing: " + nearestOther.name : ""));
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: c250747ddb036f14faf25789ceb28a34

View File

@ -1,86 +0,0 @@
using System;
using UnityEngine;
using System.Collections.Generic;
using UnityEngine.VFX;
namespace SRL
{
public class ArcadeKart : MonoBehaviour
{
public InputData Input { get; private set; }
// the input sources that can control the kart
IInput[] m_Inputs;
void GatherInputs()
{
// reset input
Input = new InputData();
// gather nonzero input from our sources
for (int i = 0; i < m_Inputs.Length; i++)
{
Input = m_Inputs[i].GenerateInput();
}
}
[Header("Hover Settings")]
public float hoverHeight = 2.0f;
public float hoverForce = 100f;
public LayerMask groundLayer;
public float hoverDampening = 5f;
[Header("Movement Settings")]
public float forwardSpeed = 50f;
public float turnSpeed = 20f;
public float strafeSpeed = 15f;
public float drag = 2f;
private Rigidbody rb;
void Awake()
{
rb = GetComponent<Rigidbody>();
m_Inputs = GetComponents<IInput>();
}
void FixedUpdate()
{
GatherInputs();
Hover();
Move();
}
void Hover()
{
// Raycast to ground to simulate hover
Ray ray = new Ray(transform.position, -transform.up);
if (Physics.Raycast(ray, out RaycastHit hit, hoverHeight * 2, groundLayer))
{
float hoverError = hoverHeight - hit.distance;
float upwardSpeed = rb.linearVelocity.y;
float appliedHoverForce = hoverError * hoverForce - upwardSpeed * hoverDampening;
rb.AddForce(Vector3.up * appliedHoverForce, ForceMode.Acceleration);
}
}
void Move()
{
// Forward movement
float forwardInput = Input.Accelerate ? 1f : 0f;
Vector3 forwardForce = transform.forward * forwardInput * forwardSpeed;
rb.AddForce(forwardForce, ForceMode.Acceleration);
// Turning (rotate around Y-axis)
float turnInput = Input.TurnInput;
rb.AddTorque(Vector3.up * turnInput * turnSpeed, ForceMode.Acceleration);
// Apply drag to reduce endless acceleration
rb.linearVelocity = Vector3.Lerp(rb.linearVelocity, Vector3.zero, drag * Time.fixedDeltaTime);
}
}
}

View File

@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 438e8be3307824743b12d4e4f02825ea

View File

@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 592688461ab994244b969670c11efe3f

View File

@ -1,24 +0,0 @@
using UnityEngine;
namespace SRL
{
public struct InputData
{
public bool Accelerate;
public bool Brake;
public float TurnInput;
}
public interface IInput
{
InputData GenerateInput();
}
public abstract class BaseInput : MonoBehaviour, IInput
{
/// <summary>
/// Override this function to generate an XY input that can be used to steer and control the car.
/// </summary>
public abstract InputData GenerateInput();
}
}

View File

@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 4dbe12014e130b54daf317a2e6be5b65

View File

@ -2,29 +2,71 @@ using UnityEngine;
public class StableHovercarController : MonoBehaviour public class StableHovercarController : MonoBehaviour
{ {
[Header("Hover Settings")]
public float hoverHeight = 3.0f; public float hoverHeight = 3.0f;
public float positionAdjustmentSpeed = 10.0f; public float positionAdjustmentSpeed = 10.0f;
public float raycastDistance = 10.0f; public float raycastDistance = 10.0f;
public LayerMask terrainLayer; public LayerMask terrainLayer;
[Header("Movement Settings")]
public float movementSpeed = 10.0f; // Max speed public float movementSpeed = 10.0f; // Max speed
public float acceleration = 5.0f; // How quickly to accelerate public float acceleration = 5.0f; // How quickly to accelerate
public float deceleration = 7.0f; // How quickly to decelerate public float deceleration = 7.0f; // How quickly to decelerate
public float rotationSpeed = 100.0f; public float rotationSpeed = 100.0f;
private float currentSpeed = 0.0f; // Speed that interpolates over time [Header("Reset Settings")]
[Tooltip("Time (in seconds) without track contact before resetting the vehicle.")]
public float timeBeforeReset = 3.0f;
// Checkpoints are used to reposition the vehicle.
public Transform[] checkpoints;
[Header("Track Orientation Settings")]
[Tooltip("Distance to search downward for track (used to align the vehicles bottom).")]
public float trackRaycastDistance = 10.0f;
[Tooltip("Tag used on the track geometry.")]
public string trackTag = "Track";
private float currentSpeed = 0.0f;
private Rigidbody rb; private Rigidbody rb;
private RacerProgress racerProgress;
// Timer for how long the raycast has failed to hit ground/track
private float noContactTimer = 0.0f;
// Flag that indicates whether a raycast hit was detected in this physics update.
private bool groundContact = false;
void Start() void Start()
{ {
rb = GetComponent<Rigidbody>(); rb = GetComponent<Rigidbody>();
rb.useGravity = false; // Disable gravity for stability rb.useGravity = false; // Disable gravity for hover stability
racerProgress = GetComponent<RacerProgress>();
if (racerProgress == null)
{
Debug.LogWarning("No RacerProgress component found on this vehicle!");
}
} }
void FixedUpdate() void FixedUpdate()
{ {
HandleHovering(); HandleHovering();
HandleMovement(); HandleMovement();
// If we arent detecting ground/track contact, count up.
if (!groundContact)
{
noContactTimer += Time.fixedDeltaTime;
if (noContactTimer >= timeBeforeReset)
{
ResetToCheckpoint();
noContactTimer = 0.0f; // Reset timer after repositioning
}
}
else
{
// Reset the timer when ground/track is detected.
noContactTimer = 0.0f;
}
} }
void HandleHovering() void HandleHovering()
@ -32,23 +74,30 @@ public class StableHovercarController : MonoBehaviour
RaycastHit hit; RaycastHit hit;
Vector3 rayOrigin = transform.position; Vector3 rayOrigin = transform.position;
// Cast a ray downward to detect the terrain // Cast a ray downward to detect the terrain (or track geometry).
if (Physics.Raycast(rayOrigin, -transform.up, out hit, raycastDistance, terrainLayer)) if (Physics.Raycast(rayOrigin, -transform.up, out hit, raycastDistance, terrainLayer))
{ {
// We have ground contact.
groundContact = true;
// Determine the triangle on the mesh that was hit.
Mesh mesh = hit.collider.GetComponent<MeshFilter>().mesh; Mesh mesh = hit.collider.GetComponent<MeshFilter>().mesh;
int triangleIndex = hit.triangleIndex; int triangleIndex = hit.triangleIndex;
int vertex1Index = mesh.triangles[triangleIndex * 3 + 0]; int vertex1Index = mesh.triangles[triangleIndex * 3 + 0];
int vertex2Index = mesh.triangles[triangleIndex * 3 + 1]; int vertex2Index = mesh.triangles[triangleIndex * 3 + 1];
int vertex3Index = mesh.triangles[triangleIndex * 3 + 2]; int vertex3Index = mesh.triangles[triangleIndex * 3 + 2];
// Convert the triangle vertices to world space.
Vector3 worldVertex1 = hit.collider.transform.TransformPoint(mesh.vertices[vertex1Index]); Vector3 worldVertex1 = hit.collider.transform.TransformPoint(mesh.vertices[vertex1Index]);
Vector3 worldVertex2 = hit.collider.transform.TransformPoint(mesh.vertices[vertex2Index]); Vector3 worldVertex2 = hit.collider.transform.TransformPoint(mesh.vertices[vertex2Index]);
Vector3 worldVertex3 = hit.collider.transform.TransformPoint(mesh.vertices[vertex3Index]); Vector3 worldVertex3 = hit.collider.transform.TransformPoint(mesh.vertices[vertex3Index]);
// Interpolate to find the hit point on the triangle.
Vector3 interpolatedPoint = worldVertex1 * hit.barycentricCoordinate.x + Vector3 interpolatedPoint = worldVertex1 * hit.barycentricCoordinate.x +
worldVertex2 * hit.barycentricCoordinate.y + worldVertex2 * hit.barycentricCoordinate.y +
worldVertex3 * hit.barycentricCoordinate.z; worldVertex3 * hit.barycentricCoordinate.z;
// Similarly, interpolate the normals.
Vector3 localNormal1 = mesh.normals[vertex1Index]; Vector3 localNormal1 = mesh.normals[vertex1Index];
Vector3 localNormal2 = mesh.normals[vertex2Index]; Vector3 localNormal2 = mesh.normals[vertex2Index];
Vector3 localNormal3 = mesh.normals[vertex3Index]; Vector3 localNormal3 = mesh.normals[vertex3Index];
@ -60,37 +109,128 @@ public class StableHovercarController : MonoBehaviour
worldNormal3 * hit.barycentricCoordinate.z; worldNormal3 * hit.barycentricCoordinate.z;
interpolatedNormal.Normalize(); interpolatedNormal.Normalize();
// Compute the target hover position.
Vector3 targetPosition = interpolatedPoint + interpolatedNormal * hoverHeight; Vector3 targetPosition = interpolatedPoint + interpolatedNormal * hoverHeight;
transform.position = Vector3.Lerp(transform.position, targetPosition, Time.fixedDeltaTime * positionAdjustmentSpeed); transform.position = Vector3.Lerp(transform.position, targetPosition, Time.fixedDeltaTime * positionAdjustmentSpeed);
// Smoothly rotate the vehicle to align with the terrain.
Quaternion targetRotation = Quaternion.FromToRotation(transform.up, interpolatedNormal) * transform.rotation; Quaternion targetRotation = Quaternion.FromToRotation(transform.up, interpolatedNormal) * transform.rotation;
rb.rotation = Quaternion.Slerp(rb.rotation, targetRotation, Time.fixedDeltaTime * 5.0f); rb.rotation = Quaternion.Slerp(rb.rotation, targetRotation, Time.fixedDeltaTime * 5.0f);
} }
else
{
// No ground detected.
groundContact = false;
}
} }
void HandleMovement() void HandleMovement()
{ {
// Get player input // Get player input.
float input = Input.GetAxis("Vertical"); float input = Input.GetAxis("Vertical");
// Determine target speed based on input // Calculate target speed.
float targetSpeed = input * movementSpeed; float targetSpeed = input * movementSpeed;
// Smooth acceleration/deceleration.
if (input != 0) if (input != 0)
{ {
// Smooth acceleration
currentSpeed = Mathf.Lerp(currentSpeed, targetSpeed, Time.fixedDeltaTime * acceleration); currentSpeed = Mathf.Lerp(currentSpeed, targetSpeed, Time.fixedDeltaTime * acceleration);
} }
else else
{ {
// Smooth deceleration when no input is present
currentSpeed = Mathf.Lerp(currentSpeed, 0, Time.fixedDeltaTime * deceleration); currentSpeed = Mathf.Lerp(currentSpeed, 0, Time.fixedDeltaTime * deceleration);
} }
// Apply movement and rotation // Apply forward movement.
rb.linearVelocity = transform.forward * currentSpeed; rb.linearVelocity = transform.forward * currentSpeed;
// Apply rotation.
float turn = Input.GetAxis("Horizontal") * rotationSpeed; float turn = Input.GetAxis("Horizontal") * rotationSpeed;
rb.angularVelocity = transform.up * turn * Mathf.Deg2Rad; rb.angularVelocity = transform.up * turn * Mathf.Deg2Rad;
} }
/// <summary>
/// Resets the vehicles position to the last checkpoint reached (from RacerProgress) and orients it so that:
/// - Its front faces the nearest (other) checkpoint.
/// - Its bottom is aligned to the track (using a raycast and the specified track tag).
/// </summary>
void ResetToCheckpoint()
{
// Make sure we have checkpoints.
if (checkpoints == null || checkpoints.Length == 0)
{
Debug.LogWarning("No checkpoints have been assigned in the Inspector.");
return;
}
if (racerProgress == null)
{
Debug.LogWarning("RacerProgress component is missing. Cannot reset to checkpoint.");
return;
}
int cpIndex = racerProgress.currentCheckpointIndex;
if (cpIndex < 0 || cpIndex >= checkpoints.Length)
{
Debug.LogWarning("Invalid checkpoint index in RacerProgress!");
return;
}
// Position: use the checkpoint that the racer last reached.
Transform lastCheckpoint = checkpoints[cpIndex];
// Find the "other" checkpoint nearest to the last checkpoint.
// (This will be used to determine the forward direction.)
Transform nearestOther = null;
float nearestDistance = Mathf.Infinity;
foreach (Transform cp in checkpoints)
{
if (cp == lastCheckpoint)
continue;
float d = Vector3.Distance(lastCheckpoint.position, cp.position);
if (d < nearestDistance)
{
nearestDistance = d;
nearestOther = cp;
}
}
// Determine desired forward:
// If we found another checkpoint, aim toward it.
// (Project the vector onto the plane defined by the desired up.)
Vector3 desiredForward = (nearestOther != null)
? (nearestOther.position - lastCheckpoint.position).normalized
: transform.forward;
// Determine the tracks surface normal so we can align the vehicles bottom.
// We cast a ray downward from a point just above the checkpoint.
Vector3 desiredUp = Vector3.up; // Fallback if no track is detected.
RaycastHit hit;
Vector3 rayOrigin = lastCheckpoint.position + Vector3.up * 1.0f;
if (Physics.Raycast(rayOrigin, -Vector3.up, out hit, trackRaycastDistance))
{
if (hit.collider.CompareTag(trackTag))
{
// To have the vehicles bottom (transform.up) flush with the track,
// we set our desired up vector to be the inverse of the tracks normal.
desiredUp = -hit.normal;
}
}
// Now adjust the desired forward so that it is perpendicular to the desired up.
desiredForward = Vector3.ProjectOnPlane(desiredForward, desiredUp).normalized;
// Build the final rotation.
Quaternion desiredRotation = Quaternion.LookRotation(desiredForward, desiredUp);
// Reset position and orientation.
transform.position = lastCheckpoint.position;
transform.rotation = desiredRotation;
// Clear any existing motion.
rb.linearVelocity = Vector3.zero;
rb.angularVelocity = Vector3.zero;
Debug.Log("Vehicle reset to checkpoint: " + lastCheckpoint.name +
(nearestOther != null ? " with front facing: " + nearestOther.name : ""));
}
} }

View File

@ -1,20 +0,0 @@
using UnityEngine;
namespace SRL {
public class KeyboardInput : BaseInput
{
public string TurnInputName = "Horizontal";
public string AccelerateButtonName = "Accelerate";
public string BrakeButtonName = "Brake";
public override InputData GenerateInput() {
return new InputData
{
Accelerate = Input.GetButton(AccelerateButtonName),
Brake = Input.GetButton(BrakeButtonName),
TurnInput = Input.GetAxis("Horizontal")
};
}
}
}

View File

@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: de1851cd35e729f4aa33011a49cd59fb

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: f060abec37829be48a8165282e7c3aeb

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 33bad5ae96b211b4ba79fa289a4eb6b9
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,24 @@
using UnityEngine;
public class Checkpoint : MonoBehaviour
{
[Tooltip("The order index of this checkpoint along the track.")]
public int checkpointIndex = 0;
private void OnTriggerEnter(Collider other)
{
RacerProgress progress = other.GetComponent<RacerProgress>();
if (progress != null && RaceManager.Instance != null)
{
// Only update if the racer is expecting this checkpoint next.
if (progress.currentCheckpointIndex == checkpointIndex)
{
int totalCheckpoints = RaceManager.Instance.numberOfCheckpoints;
progress.currentCheckpointIndex = (checkpointIndex + 1) % totalCheckpoints;
// If the racer just passed the final checkpoint, increment the lap count.
if (checkpointIndex == totalCheckpoints - 1)
progress.lapCount++;
}
}
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 7c3aaa50cd9e11d49801b7daff0a21bf

View File

@ -0,0 +1,13 @@
using UnityEngine;
public class FinishLine : MonoBehaviour
{
private void OnTriggerEnter(Collider other)
{
RacerProgress progress = other.GetComponent<RacerProgress>();
if (progress != null && RaceManager.Instance != null)
{
RaceManager.Instance.CheckFinish(progress);
}
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 9a4bc101ca357064ca138761e1319354

View File

@ -0,0 +1,72 @@
using UnityEngine;
using System.Collections.Generic;
public class RaceManager : MonoBehaviour
{
public static RaceManager Instance;
[Header("Race Settings")]
[Tooltip("Total number of laps needed to finish the race.")]
public int totalLaps = 3;
[Tooltip("Total number of checkpoints in the race.")]
public int numberOfCheckpoints = 0; // Set this in the Inspector to match your track setup
[Header("Finish Line Settings")]
[Tooltip("Assign the finish line object (with a trigger collider) here.")]
public Transform finishLine;
private List<RacerProgress> racers = new List<RacerProgress>();
public bool raceFinished = false;
public string winnerName = "";
void Awake()
{
// Basic singleton setup.
if (Instance == null)
Instance = this;
else
Destroy(gameObject);
}
void Start()
{
// Optionally, automatically find all racers by tag.
GameObject[] racerObjects = GameObject.FindGameObjectsWithTag("Racer");
foreach (GameObject racer in racerObjects)
{
RacerProgress progress = racer.GetComponent<RacerProgress>();
if (progress != null)
racers.Add(progress);
}
}
/// <summary>
/// Returns the highest “progress value” among all racers.
/// (Calculated as: lap count * numberOfCheckpoints + current checkpoint index.)
/// </summary>
public float GetLeaderProgress()
{
float leaderProgress = 0f;
foreach (RacerProgress rp in racers)
{
float progressValue = rp.lapCount * numberOfCheckpoints + rp.currentCheckpointIndex;
if (progressValue > leaderProgress)
leaderProgress = progressValue;
}
return leaderProgress;
}
/// <summary>
/// Call this when a racer crosses the finish line.
/// </summary>
public void CheckFinish(RacerProgress rp)
{
if (rp.lapCount >= totalLaps && !raceFinished)
{
raceFinished = true;
winnerName = rp.gameObject.name;
Debug.Log("Race Finished! Winner: " + winnerName);
// Here you might trigger UI updates, stop all racers, etc.
}
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 18ac36268412d1946a31d8945cc6ac33

View File

@ -0,0 +1,9 @@
using UnityEngine;
public class RacerProgress : MonoBehaviour
{
// The checkpoint index the racer is currently aiming for.
public int currentCheckpointIndex = 0;
// The current lap count.
public int lapCount = 0;
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 80f1410edd2d20447b553914aa3c9c7f

View File

@ -11,9 +11,12 @@ EditorBuildSettings:
- enabled: 0 - enabled: 0
path: Assets/Scenes/SampleScene.unity path: Assets/Scenes/SampleScene.unity
guid: 99c9720ab356a0642a771bea13969a05 guid: 99c9720ab356a0642a771bea13969a05
- enabled: 1 - enabled: 0
path: Assets/Scenes/TestTrack.unity path: Assets/Scenes/TestTrackWithAI.unity
guid: 933d22bf6aedba544849d3821a5d3a91 guid: 933d22bf6aedba544849d3821a5d3a91
- enabled: 1
path: Assets/Scenes/AITrack.unity
guid: 79f34c11850512b418ac4060394f87ce
m_configObjects: m_configObjects:
com.unity.input.settings.actions: {fileID: -944628639613478452, guid: 052faaac586de48259a63d0c4782560b, type: 3} com.unity.input.settings.actions: {fileID: -944628639613478452, guid: 052faaac586de48259a63d0c4782560b, type: 3}
m_UseUCBPForAssetBundles: 0 m_UseUCBPForAssetBundles: 0

View File

@ -3,7 +3,9 @@
--- !u!78 &1 --- !u!78 &1
TagManager: TagManager:
serializedVersion: 3 serializedVersion: 3
tags: [] tags:
- Racer
- Track
layers: layers:
- Default - Default
- TransparentFX - TransparentFX