first commit
This commit is contained in:
commit
35790cbd34
100
.editorconfig
Normal file
100
.editorconfig
Normal file
@ -0,0 +1,100 @@
|
||||
# Remove the line below if you want to inherit .editorconfig settings from higher directories
|
||||
root = true
|
||||
|
||||
# C# files
|
||||
[*.{cs,razor}]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
tab_size = 4
|
||||
|
||||
# New line preferences
|
||||
end_of_line = crlf
|
||||
insert_final_newline = true
|
||||
|
||||
|
||||
#### C# Coding Conventions ####
|
||||
|
||||
# Expression-bodied members
|
||||
csharp_style_expression_bodied_accessors = true:silent
|
||||
csharp_style_expression_bodied_constructors = false:silent
|
||||
csharp_style_expression_bodied_indexers = true:silent
|
||||
csharp_style_expression_bodied_lambdas = true:silent
|
||||
csharp_style_expression_bodied_local_functions = false:silent
|
||||
csharp_style_expression_bodied_methods = false:silent
|
||||
csharp_style_expression_bodied_operators = false:silent
|
||||
csharp_style_expression_bodied_properties = true:silent
|
||||
|
||||
# Pattern matching preferences
|
||||
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
|
||||
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
|
||||
csharp_style_prefer_not_pattern = true:suggestion
|
||||
csharp_style_prefer_pattern_matching = true:silent
|
||||
csharp_style_prefer_switch_expression = true:suggestion
|
||||
|
||||
# Null-checking preferences
|
||||
csharp_style_conditional_delegate_call = true:suggestion
|
||||
|
||||
# Code-block preferences
|
||||
csharp_prefer_braces = true:silent
|
||||
|
||||
# Expression-level preferences
|
||||
csharp_prefer_simple_default_expression = true:suggestion
|
||||
csharp_style_deconstructed_variable_declaration = true:suggestion
|
||||
csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion
|
||||
csharp_style_inlined_variable_declaration = true:suggestion
|
||||
csharp_style_pattern_local_over_anonymous_function = true:suggestion
|
||||
csharp_style_prefer_index_operator = true:suggestion
|
||||
csharp_style_prefer_range_operator = true:suggestion
|
||||
csharp_style_throw_expression = true:suggestion
|
||||
csharp_style_unused_value_assignment_preference = discard_variable:suggestion
|
||||
csharp_style_unused_value_expression_statement_preference = discard_variable:silent
|
||||
|
||||
# 'using' directive preferences
|
||||
csharp_using_directive_placement = outside_namespace:silent
|
||||
|
||||
#### C# Formatting Rules ####
|
||||
|
||||
# New line preferences
|
||||
csharp_new_line_before_catch = true
|
||||
csharp_new_line_before_else = true
|
||||
csharp_new_line_before_finally = true
|
||||
csharp_new_line_before_members_in_anonymous_types = true
|
||||
csharp_new_line_before_members_in_object_initializers = true
|
||||
csharp_new_line_before_open_brace = all
|
||||
csharp_new_line_between_query_expression_clauses = true
|
||||
|
||||
# Indentation preferences
|
||||
csharp_indent_block_contents = true
|
||||
csharp_indent_braces = false
|
||||
csharp_indent_case_contents = true
|
||||
csharp_indent_case_contents_when_block = true
|
||||
csharp_indent_labels = no_change
|
||||
csharp_indent_switch_labels = true
|
||||
|
||||
# Space preferences
|
||||
csharp_space_after_cast = false
|
||||
csharp_space_after_colon_in_inheritance_clause = true
|
||||
csharp_space_after_comma = true
|
||||
csharp_space_after_dot = false
|
||||
csharp_space_after_keywords_in_control_flow_statements = true
|
||||
csharp_space_after_semicolon_in_for_statement = true
|
||||
csharp_space_around_binary_operators = before_and_after
|
||||
csharp_space_around_declaration_statements = false
|
||||
csharp_space_before_colon_in_inheritance_clause = true
|
||||
csharp_space_before_comma = false
|
||||
csharp_space_before_dot = false
|
||||
csharp_space_before_open_square_brackets = false
|
||||
csharp_space_before_semicolon_in_for_statement = false
|
||||
csharp_space_between_empty_square_brackets = false
|
||||
csharp_space_between_method_call_empty_parameter_list_parentheses = false
|
||||
csharp_space_between_method_call_name_and_opening_parenthesis = false
|
||||
csharp_space_between_method_call_parameter_list_parentheses = true
|
||||
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
|
||||
csharp_space_between_method_declaration_name_and_open_parenthesis = false
|
||||
csharp_space_between_method_declaration_parameter_list_parentheses = true
|
||||
csharp_space_between_parentheses = control_flow_statements
|
||||
csharp_space_between_square_brackets = false
|
||||
|
||||
# Wrapping preferences
|
||||
csharp_preserve_single_line_blocks = true
|
||||
csharp_preserve_single_line_statements = true
|
||||
28
.gitignore
vendored
Normal file
28
.gitignore
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
|
||||
# This file describes files and paths that should not be tracked by Git version control
|
||||
# https://git-scm.com/docs/gitignore
|
||||
|
||||
# Auto-generated code editor files
|
||||
.vs/*
|
||||
.vscode/*
|
||||
*.csproj
|
||||
obj
|
||||
bin
|
||||
Properties/*
|
||||
code/obj/*
|
||||
code/Properties/*
|
||||
|
||||
# Auto-generated asset related files
|
||||
.sbox/*
|
||||
*.generated.*
|
||||
*.*_c
|
||||
!*.shader_c
|
||||
*.los
|
||||
*.vpk
|
||||
*launchSettings.json
|
||||
*.sln
|
||||
|
||||
*idea
|
||||
|
||||
# Exported / standalone games
|
||||
Exports/
|
||||
22
Assets/Materials/library_material.vmat
Normal file
22
Assets/Materials/library_material.vmat
Normal file
@ -0,0 +1,22 @@
|
||||
"Layer0"
|
||||
{
|
||||
"shader" "shaders/complex.shader"
|
||||
"g_flAmbientOcclusionDirectDiffuse" "0.000000"
|
||||
"g_flAmbientOcclusionDirectSpecular" "0.000000"
|
||||
"TextureAmbientOcclusion" "materials/default/default_ao.tga"
|
||||
"g_flModelTintAmount" "1.000000"
|
||||
"g_vColorTint" "[1.000000 1.000000 1.000000 0.000000]"
|
||||
"TextureColor" "materials/default/default_color.tga"
|
||||
"g_flFadeExponent" "1.000000"
|
||||
"g_bFogEnabled" "1"
|
||||
"g_flDirectionalLightmapMinZ" "0.050000"
|
||||
"g_flDirectionalLightmapStrength" "1.000000"
|
||||
"g_flMetalness" "0.000000"
|
||||
"TextureNormal" "materials/default/default_normal.tga"
|
||||
"TextureRoughness" "materials/default/default_rough.tga"
|
||||
"g_nScaleTexCoordUByModelScaleAxis" "0"
|
||||
"g_nScaleTexCoordVByModelScaleAxis" "0"
|
||||
"g_vTexCoordOffset" "[0.000 0.000]"
|
||||
"g_vTexCoordScale" "[1.000 1.000]"
|
||||
"g_vTexCoordScrollSpeed" "[0.000 0.000]"
|
||||
}
|
||||
363
Assets/effects/metal_impact.prefab
Normal file
363
Assets/effects/metal_impact.prefab
Normal file
@ -0,0 +1,363 @@
|
||||
{
|
||||
"RootObject": {
|
||||
"__guid": "bbc29007-9ae0-455d-9bde-62bae3dd88e9",
|
||||
"__version": 1,
|
||||
"Flags": 0,
|
||||
"Name": "metal_impact",
|
||||
"Position": "0,0,0",
|
||||
"Rotation": "0,0,0,1",
|
||||
"Scale": "1,1,1",
|
||||
"Tags": "particles",
|
||||
"Enabled": true,
|
||||
"NetworkMode": 2,
|
||||
"NetworkInterpolation": true,
|
||||
"NetworkOrphaned": 0,
|
||||
"OwnerTransfer": 1,
|
||||
"Components": [
|
||||
{
|
||||
"__type": "Sandbox.ParticleEffect",
|
||||
"__guid": "4e3d505f-af05-4c8f-85ee-c5a264ff23b6",
|
||||
"__enabled": true,
|
||||
"__version": 1,
|
||||
"Alpha": {
|
||||
"Type": "Curve",
|
||||
"Evaluation": "Life",
|
||||
"CurveA": [
|
||||
{
|
||||
"x": 0,
|
||||
"y": 1,
|
||||
"in": 0,
|
||||
"out": 0,
|
||||
"mode": "Mirrored"
|
||||
},
|
||||
{
|
||||
"x": 1,
|
||||
"y": 0,
|
||||
"in": 0,
|
||||
"out": 0,
|
||||
"mode": "Mirrored"
|
||||
}
|
||||
],
|
||||
"CurveB": [
|
||||
{
|
||||
"x": 0.5,
|
||||
"y": 0.5,
|
||||
"in": 0,
|
||||
"out": 0,
|
||||
"mode": "Mirrored"
|
||||
}
|
||||
],
|
||||
"Constants": "1,0,0,0"
|
||||
},
|
||||
"ApplyAlpha": false,
|
||||
"ApplyColor": true,
|
||||
"ApplyRotation": true,
|
||||
"ApplyShape": true,
|
||||
"Bounce": 1,
|
||||
"Brightness": 1,
|
||||
"Bumpiness": 0,
|
||||
"Collision": false,
|
||||
"CollisionIgnore": "player,vehicle",
|
||||
"CollisionPrefab": [],
|
||||
"CollisionPrefabAlign": false,
|
||||
"CollisionPrefabChance": 1,
|
||||
"CollisionPrefabRotation": 0,
|
||||
"CollisionRadius": 1,
|
||||
"Damping": 0,
|
||||
"DieOnCollisionChance": 0,
|
||||
"FollowerPrefab": [],
|
||||
"FollowerPrefabChance": 1,
|
||||
"FollowerPrefabKill": true,
|
||||
"Force": true,
|
||||
"ForceDirection": "0,0,-300",
|
||||
"ForceScale": 1,
|
||||
"ForceSpace": "World",
|
||||
"Friction": 1,
|
||||
"Gradient": {
|
||||
"Type": "Constant",
|
||||
"Evaluation": "Particle",
|
||||
"GradientA": {
|
||||
"blend": "Linear",
|
||||
"color": [
|
||||
{
|
||||
"t": 0,
|
||||
"c": "0.79581,0.8093,0,1"
|
||||
},
|
||||
{
|
||||
"t": 0.33,
|
||||
"c": "1,0.93333,0,1"
|
||||
},
|
||||
{
|
||||
"t": 0.66,
|
||||
"c": "1,0.64706,0,1"
|
||||
},
|
||||
{
|
||||
"t": 1,
|
||||
"c": "1,1,0,1"
|
||||
},
|
||||
{
|
||||
"t": 1,
|
||||
"c": "1,0.55,0,1"
|
||||
}
|
||||
],
|
||||
"alpha": [
|
||||
{
|
||||
"t": 0,
|
||||
"a": 1
|
||||
},
|
||||
{
|
||||
"t": 1,
|
||||
"a": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"GradientB": {
|
||||
"blend": "Linear",
|
||||
"color": [
|
||||
{
|
||||
"t": 0.5,
|
||||
"c": "1,1,1,1"
|
||||
}
|
||||
],
|
||||
"alpha": []
|
||||
},
|
||||
"ConstantA": "1,1,1,1",
|
||||
"ConstantB": "1,0,0,1"
|
||||
},
|
||||
"Lifetime": {
|
||||
"Type": "Range",
|
||||
"Evaluation": "Particle",
|
||||
"CurveA": [
|
||||
{
|
||||
"x": 0.5,
|
||||
"y": 0.5,
|
||||
"in": 0,
|
||||
"out": 0,
|
||||
"mode": "Mirrored"
|
||||
}
|
||||
],
|
||||
"CurveB": [
|
||||
{
|
||||
"x": 0.5,
|
||||
"y": 0.5,
|
||||
"in": 0,
|
||||
"out": 0,
|
||||
"mode": "Mirrored"
|
||||
}
|
||||
],
|
||||
"Constants": "0.1,2,0,0"
|
||||
},
|
||||
"MaxParticles": 500,
|
||||
"OnComponentDestroy": null,
|
||||
"OnComponentDisabled": null,
|
||||
"OnComponentEnabled": null,
|
||||
"OnComponentFixedUpdate": null,
|
||||
"OnComponentStart": null,
|
||||
"OnComponentUpdate": null,
|
||||
"OnParticleCreated": null,
|
||||
"OnParticleDestroyed": null,
|
||||
"OrbitalForce": {
|
||||
"X": 0,
|
||||
"Y": 0,
|
||||
"Z": 0
|
||||
},
|
||||
"OrbitalPull": 0,
|
||||
"PerParticleTimeScale": 1,
|
||||
"Pitch": 0,
|
||||
"PreWarm": 0,
|
||||
"PushStrength": 0,
|
||||
"Roll": {
|
||||
"Type": "Range",
|
||||
"Evaluation": "Particle",
|
||||
"CurveA": [
|
||||
{
|
||||
"x": 0.5,
|
||||
"y": 0.5,
|
||||
"in": 0,
|
||||
"out": 0,
|
||||
"mode": "Mirrored"
|
||||
}
|
||||
],
|
||||
"CurveB": [
|
||||
{
|
||||
"x": 0.5,
|
||||
"y": 0.5,
|
||||
"in": 0,
|
||||
"out": 0,
|
||||
"mode": "Mirrored"
|
||||
}
|
||||
],
|
||||
"Constants": "0,360,0,0"
|
||||
},
|
||||
"Scale": {
|
||||
"Type": "Curve",
|
||||
"Evaluation": "Life",
|
||||
"CurveA": [
|
||||
{
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"in": -9.333329,
|
||||
"out": 9.333329,
|
||||
"mode": "Mirrored"
|
||||
},
|
||||
{
|
||||
"x": 0.12527578,
|
||||
"y": 1,
|
||||
"in": -0.056075174,
|
||||
"out": 0.056075174,
|
||||
"mode": "Mirrored"
|
||||
},
|
||||
{
|
||||
"x": 0.36042944,
|
||||
"y": 0.40260965,
|
||||
"in": 1.4467248,
|
||||
"out": -1.4467248,
|
||||
"mode": "Mirrored"
|
||||
},
|
||||
{
|
||||
"x": 1,
|
||||
"y": 0,
|
||||
"in": -0.39285707,
|
||||
"out": 0.39285707,
|
||||
"mode": "Mirrored"
|
||||
}
|
||||
],
|
||||
"CurveB": [
|
||||
{
|
||||
"x": 0.5,
|
||||
"y": 0.5,
|
||||
"in": 0,
|
||||
"out": 0,
|
||||
"mode": "Mirrored"
|
||||
}
|
||||
],
|
||||
"Constants": "1,0,0,0"
|
||||
},
|
||||
"SequenceId": 0,
|
||||
"SequenceSpeed": 1,
|
||||
"SequenceTime": 1,
|
||||
"SheetSequence": false,
|
||||
"Space": "World",
|
||||
"StartDelay": 0,
|
||||
"StartVelocity": {
|
||||
"Type": "Range",
|
||||
"Evaluation": "Particle",
|
||||
"CurveA": [
|
||||
{
|
||||
"x": 0.5,
|
||||
"y": 0.5,
|
||||
"in": 0,
|
||||
"out": 0,
|
||||
"mode": "Mirrored"
|
||||
}
|
||||
],
|
||||
"CurveB": [
|
||||
{
|
||||
"x": 0.5,
|
||||
"y": 0.5,
|
||||
"in": 0,
|
||||
"out": 0,
|
||||
"mode": "Mirrored"
|
||||
}
|
||||
],
|
||||
"Constants": "10,70,0,0"
|
||||
},
|
||||
"Stretch": 0,
|
||||
"TimeScale": 1,
|
||||
"Timing": "GameTime",
|
||||
"Tint": "1,1,1,1",
|
||||
"UsePrefabFeature": false,
|
||||
"Yaw": 0
|
||||
},
|
||||
{
|
||||
"__type": "Sandbox.ParticleSpriteRenderer",
|
||||
"__guid": "53350178-5acf-40f4-82a7-fc9cf56f9209",
|
||||
"__enabled": true,
|
||||
"Additive": false,
|
||||
"Alignment": "LookAtCamera",
|
||||
"BlurAmount": 1,
|
||||
"BlurOpacity": 0.61,
|
||||
"BlurSpacing": 1,
|
||||
"DepthFeather": 0,
|
||||
"FaceVelocity": false,
|
||||
"FogStrength": 1,
|
||||
"LeadingTrail": true,
|
||||
"Lighting": true,
|
||||
"MotionBlur": true,
|
||||
"OnComponentDestroy": null,
|
||||
"OnComponentDisabled": null,
|
||||
"OnComponentEnabled": null,
|
||||
"OnComponentFixedUpdate": null,
|
||||
"OnComponentStart": null,
|
||||
"OnComponentUpdate": null,
|
||||
"Opaque": false,
|
||||
"Pivot": "0.5,0.5",
|
||||
"RenderOptions": {
|
||||
"GameLayer": true,
|
||||
"OverlayLayer": false,
|
||||
"BloomLayer": false,
|
||||
"AfterUILayer": false
|
||||
},
|
||||
"RotationOffset": 0,
|
||||
"Scale": 1,
|
||||
"Shadows": false,
|
||||
"SortMode": "Unsorted",
|
||||
"Texture": "materials/particles/shapes/spark2.vtex"
|
||||
},
|
||||
{
|
||||
"__type": "Sandbox.ParticleConeEmitter",
|
||||
"__guid": "e190fd95-5525-4287-9587-ad1a46170627",
|
||||
"__enabled": true,
|
||||
"Burst": 50,
|
||||
"CenterBias": 0,
|
||||
"CenterBiasVelocity": 0,
|
||||
"ConeAngle": 30,
|
||||
"ConeFar": 1,
|
||||
"ConeNear": 1,
|
||||
"Delay": 0,
|
||||
"DestroyOnEnd": true,
|
||||
"Duration": 1,
|
||||
"InVolume": false,
|
||||
"Loop": false,
|
||||
"OnComponentDestroy": null,
|
||||
"OnComponentDisabled": null,
|
||||
"OnComponentEnabled": null,
|
||||
"OnComponentFixedUpdate": null,
|
||||
"OnComponentStart": null,
|
||||
"OnComponentUpdate": null,
|
||||
"OnEdge": false,
|
||||
"Rate": 0,
|
||||
"RateOverDistance": 0,
|
||||
"VelocityMultiplier": 1,
|
||||
"VelocityRandom": 0
|
||||
}
|
||||
],
|
||||
"Children": [],
|
||||
"__properties": {
|
||||
"NetworkInterpolation": true,
|
||||
"TimeScale": 1,
|
||||
"WantsSystemScene": true,
|
||||
"Metadata": {},
|
||||
"NavMesh": {
|
||||
"Enabled": false,
|
||||
"IncludeStaticBodies": true,
|
||||
"IncludeKeyframedBodies": true,
|
||||
"EditorAutoUpdate": true,
|
||||
"AgentHeight": 64,
|
||||
"AgentRadius": 16,
|
||||
"AgentStepSize": 18,
|
||||
"AgentMaxSlope": 40,
|
||||
"ExcludedBodies": "",
|
||||
"IncludedBodies": ""
|
||||
}
|
||||
},
|
||||
"__variables": []
|
||||
},
|
||||
"ResourceVersion": 2,
|
||||
"ShowInMenu": false,
|
||||
"MenuPath": null,
|
||||
"MenuIcon": null,
|
||||
"DontBreakAsTemplate": false,
|
||||
"__references": [],
|
||||
"__version": 2
|
||||
}
|
||||
BIN
Assets/sounds/collisions/boat_heavy_1.wav
Normal file
BIN
Assets/sounds/collisions/boat_heavy_1.wav
Normal file
Binary file not shown.
BIN
Assets/sounds/collisions/boat_heavy_2.wav
Normal file
BIN
Assets/sounds/collisions/boat_heavy_2.wav
Normal file
Binary file not shown.
BIN
Assets/sounds/collisions/boat_heavy_3.wav
Normal file
BIN
Assets/sounds/collisions/boat_heavy_3.wav
Normal file
Binary file not shown.
BIN
Assets/sounds/collisions/body_1.wav
Normal file
BIN
Assets/sounds/collisions/body_1.wav
Normal file
Binary file not shown.
BIN
Assets/sounds/collisions/body_2.wav
Normal file
BIN
Assets/sounds/collisions/body_2.wav
Normal file
Binary file not shown.
BIN
Assets/sounds/collisions/body_3.wav
Normal file
BIN
Assets/sounds/collisions/body_3.wav
Normal file
Binary file not shown.
BIN
Assets/sounds/collisions/body_4.wav
Normal file
BIN
Assets/sounds/collisions/body_4.wav
Normal file
Binary file not shown.
BIN
Assets/sounds/collisions/body_5.wav
Normal file
BIN
Assets/sounds/collisions/body_5.wav
Normal file
Binary file not shown.
BIN
Assets/sounds/collisions/body_6.wav
Normal file
BIN
Assets/sounds/collisions/body_6.wav
Normal file
Binary file not shown.
59
Assets/sounds/collisions/car_heavy.sound
Normal file
59
Assets/sounds/collisions/car_heavy.sound
Normal file
@ -0,0 +1,59 @@
|
||||
{
|
||||
"UI": false,
|
||||
"Volume": "1",
|
||||
"Pitch": "0.8 1.2",
|
||||
"Decibels": 70,
|
||||
"SelectionMode": "Random",
|
||||
"Sounds": [
|
||||
"sounds/collisions/car_heavy_6.vsnd",
|
||||
"sounds/collisions/car_heavy_5.vsnd",
|
||||
"sounds/collisions/car_heavy_4.vsnd",
|
||||
"sounds/collisions/car_heavy_3.vsnd",
|
||||
"sounds/collisions/car_heavy_2.vsnd",
|
||||
"sounds/collisions/car_heavy_1.vsnd",
|
||||
"sounds/collisions/car_heavy_7.vsnd",
|
||||
"sounds/collisions/car_heavy_8.vsnd"
|
||||
],
|
||||
"Occlusion": true,
|
||||
"AirAbsorption": true,
|
||||
"Transmission": true,
|
||||
"OcclusionRadius": 64,
|
||||
"DistanceAttenuation": true,
|
||||
"Distance": 8500,
|
||||
"Falloff": [
|
||||
{
|
||||
"x": 0,
|
||||
"y": 1,
|
||||
"in": 0,
|
||||
"out": -1.8,
|
||||
"mode": "Mirrored"
|
||||
},
|
||||
{
|
||||
"x": 0.05,
|
||||
"y": 0.22,
|
||||
"in": 3.5,
|
||||
"out": -3.5,
|
||||
"mode": "Mirrored"
|
||||
},
|
||||
{
|
||||
"x": 0.2,
|
||||
"y": 0.04,
|
||||
"in": 0.16,
|
||||
"out": -0.16,
|
||||
"mode": "Mirrored"
|
||||
},
|
||||
{
|
||||
"x": 1,
|
||||
"y": 0,
|
||||
"in": 0,
|
||||
"out": 0,
|
||||
"mode": "Mirrored"
|
||||
}
|
||||
],
|
||||
"DefaultMixer": {
|
||||
"Name": "game",
|
||||
"Id": "37db45ef-34ec-4948-b1d7-4509bc8e97e2"
|
||||
},
|
||||
"__references": [],
|
||||
"__version": 1
|
||||
}
|
||||
BIN
Assets/sounds/collisions/car_heavy_1.wav
Normal file
BIN
Assets/sounds/collisions/car_heavy_1.wav
Normal file
Binary file not shown.
BIN
Assets/sounds/collisions/car_heavy_2.wav
Normal file
BIN
Assets/sounds/collisions/car_heavy_2.wav
Normal file
Binary file not shown.
BIN
Assets/sounds/collisions/car_heavy_3.wav
Normal file
BIN
Assets/sounds/collisions/car_heavy_3.wav
Normal file
Binary file not shown.
BIN
Assets/sounds/collisions/car_heavy_4.wav
Normal file
BIN
Assets/sounds/collisions/car_heavy_4.wav
Normal file
Binary file not shown.
BIN
Assets/sounds/collisions/car_heavy_5.wav
Normal file
BIN
Assets/sounds/collisions/car_heavy_5.wav
Normal file
Binary file not shown.
BIN
Assets/sounds/collisions/car_heavy_6.wav
Normal file
BIN
Assets/sounds/collisions/car_heavy_6.wav
Normal file
Binary file not shown.
BIN
Assets/sounds/collisions/car_heavy_7.wav
Normal file
BIN
Assets/sounds/collisions/car_heavy_7.wav
Normal file
Binary file not shown.
BIN
Assets/sounds/collisions/car_heavy_8.wav
Normal file
BIN
Assets/sounds/collisions/car_heavy_8.wav
Normal file
Binary file not shown.
55
Assets/sounds/collisions/car_light.sound
Normal file
55
Assets/sounds/collisions/car_light.sound
Normal file
@ -0,0 +1,55 @@
|
||||
{
|
||||
"UI": false,
|
||||
"Volume": "1",
|
||||
"Pitch": "0.8 1.2",
|
||||
"Decibels": 70,
|
||||
"SelectionMode": "Random",
|
||||
"Sounds": [
|
||||
"sounds/collisions/car_light_4.vsnd",
|
||||
"sounds/collisions/car_light_3.vsnd",
|
||||
"sounds/collisions/car_light_2.vsnd",
|
||||
"sounds/collisions/car_light_1.vsnd"
|
||||
],
|
||||
"Occlusion": true,
|
||||
"AirAbsorption": true,
|
||||
"Transmission": true,
|
||||
"OcclusionRadius": 64,
|
||||
"DistanceAttenuation": true,
|
||||
"Distance": 8500,
|
||||
"Falloff": [
|
||||
{
|
||||
"x": 0,
|
||||
"y": 1,
|
||||
"in": 0,
|
||||
"out": -1.8,
|
||||
"mode": "Mirrored"
|
||||
},
|
||||
{
|
||||
"x": 0.05,
|
||||
"y": 0.22,
|
||||
"in": 3.5,
|
||||
"out": -3.5,
|
||||
"mode": "Mirrored"
|
||||
},
|
||||
{
|
||||
"x": 0.2,
|
||||
"y": 0.04,
|
||||
"in": 0.16,
|
||||
"out": -0.16,
|
||||
"mode": "Mirrored"
|
||||
},
|
||||
{
|
||||
"x": 1,
|
||||
"y": 0,
|
||||
"in": 0,
|
||||
"out": 0,
|
||||
"mode": "Mirrored"
|
||||
}
|
||||
],
|
||||
"DefaultMixer": {
|
||||
"Name": "game",
|
||||
"Id": "37db45ef-34ec-4948-b1d7-4509bc8e97e2"
|
||||
},
|
||||
"__references": [],
|
||||
"__version": 1
|
||||
}
|
||||
BIN
Assets/sounds/collisions/car_light_1.wav
Normal file
BIN
Assets/sounds/collisions/car_light_1.wav
Normal file
Binary file not shown.
BIN
Assets/sounds/collisions/car_light_2.wav
Normal file
BIN
Assets/sounds/collisions/car_light_2.wav
Normal file
Binary file not shown.
BIN
Assets/sounds/collisions/car_light_3.wav
Normal file
BIN
Assets/sounds/collisions/car_light_3.wav
Normal file
Binary file not shown.
BIN
Assets/sounds/collisions/car_light_4.wav
Normal file
BIN
Assets/sounds/collisions/car_light_4.wav
Normal file
Binary file not shown.
BIN
Assets/sounds/collisions/land_on_water_1.wav
Normal file
BIN
Assets/sounds/collisions/land_on_water_1.wav
Normal file
Binary file not shown.
BIN
Assets/sounds/collisions/land_on_water_2.wav
Normal file
BIN
Assets/sounds/collisions/land_on_water_2.wav
Normal file
Binary file not shown.
BIN
Assets/sounds/collisions/land_on_water_3.wav
Normal file
BIN
Assets/sounds/collisions/land_on_water_3.wav
Normal file
Binary file not shown.
BIN
Assets/sounds/collisions/land_on_water_4.wav
Normal file
BIN
Assets/sounds/collisions/land_on_water_4.wav
Normal file
Binary file not shown.
53
Assets/sounds/collisions/metal_scrape.sound
Normal file
53
Assets/sounds/collisions/metal_scrape.sound
Normal file
@ -0,0 +1,53 @@
|
||||
{
|
||||
"UI": false,
|
||||
"Volume": "1",
|
||||
"Pitch": "0.8 1.2",
|
||||
"Decibels": 70,
|
||||
"SelectionMode": "Random",
|
||||
"Sounds": [
|
||||
"sounds/collisions/metal_scrape_1.vsnd",
|
||||
"sounds/collisions/metal_scrape_2.vsnd"
|
||||
],
|
||||
"Occlusion": true,
|
||||
"AirAbsorption": true,
|
||||
"Transmission": true,
|
||||
"OcclusionRadius": 64,
|
||||
"DistanceAttenuation": true,
|
||||
"Distance": 8500,
|
||||
"Falloff": [
|
||||
{
|
||||
"x": 0,
|
||||
"y": 1,
|
||||
"in": 0,
|
||||
"out": -1.8,
|
||||
"mode": "Mirrored"
|
||||
},
|
||||
{
|
||||
"x": 0.05,
|
||||
"y": 0.22,
|
||||
"in": 3.5,
|
||||
"out": -3.5,
|
||||
"mode": "Mirrored"
|
||||
},
|
||||
{
|
||||
"x": 0.2,
|
||||
"y": 0.04,
|
||||
"in": 0.16,
|
||||
"out": -0.16,
|
||||
"mode": "Mirrored"
|
||||
},
|
||||
{
|
||||
"x": 1,
|
||||
"y": 0,
|
||||
"in": 0,
|
||||
"out": 0,
|
||||
"mode": "Mirrored"
|
||||
}
|
||||
],
|
||||
"DefaultMixer": {
|
||||
"Name": "music",
|
||||
"Id": "ee991a4a-8016-40e5-93e6-cbf0d964a401"
|
||||
},
|
||||
"__references": [],
|
||||
"__version": 1
|
||||
}
|
||||
BIN
Assets/sounds/collisions/metal_scrape_1.wav
Normal file
BIN
Assets/sounds/collisions/metal_scrape_1.wav
Normal file
Binary file not shown.
BIN
Assets/sounds/collisions/metal_scrape_2.wav
Normal file
BIN
Assets/sounds/collisions/metal_scrape_2.wav
Normal file
Binary file not shown.
BIN
Assets/sounds/streams/pickup/high.wav
Normal file
BIN
Assets/sounds/streams/pickup/high.wav
Normal file
Binary file not shown.
8
Assets/sounds/streams/pickup/high.wav.meta
Normal file
8
Assets/sounds/streams/pickup/high.wav.meta
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"loop": true,
|
||||
"start": 0,
|
||||
"end": 0,
|
||||
"rate": 44100,
|
||||
"compress": false,
|
||||
"bitrate": 256
|
||||
}
|
||||
BIN
Assets/sounds/streams/pickup/idle.wav
Normal file
BIN
Assets/sounds/streams/pickup/idle.wav
Normal file
Binary file not shown.
8
Assets/sounds/streams/pickup/idle.wav.meta
Normal file
8
Assets/sounds/streams/pickup/idle.wav.meta
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"loop": true,
|
||||
"start": 0,
|
||||
"end": 0,
|
||||
"rate": 44100,
|
||||
"compress": false,
|
||||
"bitrate": 256
|
||||
}
|
||||
BIN
Assets/sounds/streams/pickup/low.wav
Normal file
BIN
Assets/sounds/streams/pickup/low.wav
Normal file
Binary file not shown.
8
Assets/sounds/streams/pickup/low.wav.meta
Normal file
8
Assets/sounds/streams/pickup/low.wav.meta
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"loop": true,
|
||||
"start": 0,
|
||||
"end": 0,
|
||||
"rate": 44100,
|
||||
"compress": false,
|
||||
"bitrate": 256
|
||||
}
|
||||
139
Assets/sounds/streams/pickup/pickup.engstr
Normal file
139
Assets/sounds/streams/pickup/pickup.engstr
Normal file
@ -0,0 +1,139 @@
|
||||
{
|
||||
"Parameters": {
|
||||
"Pitch": 1,
|
||||
"Volume": 1,
|
||||
"FadeDist": 1500,
|
||||
"RedlineFrequency": 55,
|
||||
"RedlineStrength": 0.2,
|
||||
"WobbleFrequency": 25,
|
||||
"WobbleStrength": 0.13
|
||||
},
|
||||
"Layers": {
|
||||
"rpm_high": {
|
||||
"AudioPath": "sounds/streams/pickup/high.vsnd",
|
||||
"UseRedline": true,
|
||||
"Controllers": [
|
||||
[
|
||||
"Throttle",
|
||||
0,
|
||||
1,
|
||||
"Volume",
|
||||
0,
|
||||
1
|
||||
],
|
||||
[
|
||||
"RpmFraction",
|
||||
0,
|
||||
1,
|
||||
"Pitch",
|
||||
0.49,
|
||||
1.05
|
||||
],
|
||||
[
|
||||
"RpmFraction",
|
||||
0.2,
|
||||
0.75,
|
||||
"Volume",
|
||||
0,
|
||||
1
|
||||
]
|
||||
]
|
||||
},
|
||||
"idle": {
|
||||
"AudioPath": "sounds/streams/pickup/idle.vsnd",
|
||||
"UseRedline": true,
|
||||
"Controllers": [
|
||||
[
|
||||
"Throttle",
|
||||
0,
|
||||
1,
|
||||
"Volume",
|
||||
1,
|
||||
0
|
||||
],
|
||||
[
|
||||
"RpmFraction",
|
||||
0,
|
||||
0.3,
|
||||
"Volume",
|
||||
1,
|
||||
0
|
||||
],
|
||||
[
|
||||
"RpmFraction",
|
||||
0,
|
||||
0.38,
|
||||
"Pitch",
|
||||
1,
|
||||
2
|
||||
]
|
||||
]
|
||||
},
|
||||
"rpm_low": {
|
||||
"AudioPath": "sounds/streams/pickup/low.vsnd",
|
||||
"UseRedline": true,
|
||||
"Controllers": [
|
||||
[
|
||||
"Throttle",
|
||||
0,
|
||||
1,
|
||||
"Volume",
|
||||
0,
|
||||
1
|
||||
],
|
||||
[
|
||||
"RpmFraction",
|
||||
0,
|
||||
1,
|
||||
"Pitch",
|
||||
0.75,
|
||||
1.59
|
||||
],
|
||||
[
|
||||
"RpmFraction",
|
||||
0.3,
|
||||
0.85,
|
||||
"Volume",
|
||||
1,
|
||||
0
|
||||
]
|
||||
]
|
||||
},
|
||||
"throttle_low": {
|
||||
"AudioPath": "sounds/streams/pickup/throttle_low.vsnd",
|
||||
"UseRedline": true,
|
||||
"Controllers": [
|
||||
[
|
||||
"Throttle",
|
||||
0.35,
|
||||
1,
|
||||
"Volume",
|
||||
0.85,
|
||||
0
|
||||
],
|
||||
[
|
||||
"RpmFraction",
|
||||
0,
|
||||
1,
|
||||
"Pitch",
|
||||
0.63,
|
||||
1.33
|
||||
],
|
||||
[
|
||||
"RpmFraction",
|
||||
0,
|
||||
0.2,
|
||||
"Volume",
|
||||
0,
|
||||
1
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"Parent": null,
|
||||
"Throttle": 0,
|
||||
"RpmFraction": 0,
|
||||
"IsRedlining": false,
|
||||
"__references": [],
|
||||
"__version": 0
|
||||
}
|
||||
BIN
Assets/sounds/streams/pickup/throttle_low.wav
Normal file
BIN
Assets/sounds/streams/pickup/throttle_low.wav
Normal file
Binary file not shown.
8
Assets/sounds/streams/pickup/throttle_low.wav.meta
Normal file
8
Assets/sounds/streams/pickup/throttle_low.wav.meta
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"loop": true,
|
||||
"start": 0,
|
||||
"end": 0,
|
||||
"rate": 44100,
|
||||
"compress": false,
|
||||
"bitrate": 256
|
||||
}
|
||||
BIN
Assets/sounds/streams/v8_c63/Acc_1560_ADPCM.wav
Normal file
BIN
Assets/sounds/streams/v8_c63/Acc_1560_ADPCM.wav
Normal file
Binary file not shown.
BIN
Assets/sounds/streams/v8_c63/Acc_2197_ADPCM.wav
Normal file
BIN
Assets/sounds/streams/v8_c63/Acc_2197_ADPCM.wav
Normal file
Binary file not shown.
BIN
Assets/sounds/streams/v8_c63/Acc_4130_ADPCM.wav
Normal file
BIN
Assets/sounds/streams/v8_c63/Acc_4130_ADPCM.wav
Normal file
Binary file not shown.
BIN
Assets/sounds/streams/v8_c63/Acc_6803_ADPCM.wav
Normal file
BIN
Assets/sounds/streams/v8_c63/Acc_6803_ADPCM.wav
Normal file
Binary file not shown.
BIN
Assets/sounds/streams/v8_c63/Dec_3951_ADPCM.wav
Normal file
BIN
Assets/sounds/streams/v8_c63/Dec_3951_ADPCM.wav
Normal file
Binary file not shown.
8
Assets/sounds/streams/v8_c63/acc_1560_adpcm.wav.meta
Normal file
8
Assets/sounds/streams/v8_c63/acc_1560_adpcm.wav.meta
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"loop": true,
|
||||
"start": 0,
|
||||
"end": 0,
|
||||
"rate": 44100,
|
||||
"compress": false,
|
||||
"bitrate": 256
|
||||
}
|
||||
8
Assets/sounds/streams/v8_c63/acc_2197_adpcm.wav.meta
Normal file
8
Assets/sounds/streams/v8_c63/acc_2197_adpcm.wav.meta
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"loop": true,
|
||||
"start": 0,
|
||||
"end": 0,
|
||||
"rate": 44100,
|
||||
"compress": false,
|
||||
"bitrate": 256
|
||||
}
|
||||
8
Assets/sounds/streams/v8_c63/acc_4130_adpcm.wav.meta
Normal file
8
Assets/sounds/streams/v8_c63/acc_4130_adpcm.wav.meta
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"loop": true,
|
||||
"start": 0,
|
||||
"end": 0,
|
||||
"rate": 44100,
|
||||
"compress": false,
|
||||
"bitrate": 256
|
||||
}
|
||||
8
Assets/sounds/streams/v8_c63/acc_6803_adpcm.wav.meta
Normal file
8
Assets/sounds/streams/v8_c63/acc_6803_adpcm.wav.meta
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"loop": true,
|
||||
"start": 0,
|
||||
"end": 0,
|
||||
"rate": 44100,
|
||||
"compress": false,
|
||||
"bitrate": 256
|
||||
}
|
||||
8
Assets/sounds/streams/v8_c63/dec_3951_adpcm.wav.meta
Normal file
8
Assets/sounds/streams/v8_c63/dec_3951_adpcm.wav.meta
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"loop": true,
|
||||
"start": 0,
|
||||
"end": 0,
|
||||
"rate": 44100,
|
||||
"compress": false,
|
||||
"bitrate": 256
|
||||
}
|
||||
174
Assets/sounds/streams/v8_c63/v8_c63.engstr
Normal file
174
Assets/sounds/streams/v8_c63/v8_c63.engstr
Normal file
@ -0,0 +1,174 @@
|
||||
{
|
||||
"Parameters": {
|
||||
"Pitch": 1,
|
||||
"Volume": 1,
|
||||
"FadeDist": 1500,
|
||||
"RedlineFrequency": 55,
|
||||
"RedlineStrength": 0.2,
|
||||
"WobbleFrequency": 25,
|
||||
"WobbleStrength": 0.13
|
||||
},
|
||||
"Layers": {
|
||||
"mid": {
|
||||
"AudioPath": "sounds/streams/v8_c63/acc_4130_adpcm.vsnd",
|
||||
"UseRedline": true,
|
||||
"Controllers": [
|
||||
[
|
||||
"Throttle",
|
||||
0,
|
||||
1,
|
||||
"Volume",
|
||||
0.1,
|
||||
1.1
|
||||
],
|
||||
[
|
||||
"RpmFraction",
|
||||
0,
|
||||
1,
|
||||
"Pitch",
|
||||
0.71,
|
||||
1.26
|
||||
],
|
||||
[
|
||||
"RpmFraction",
|
||||
0.79,
|
||||
1,
|
||||
"Volume",
|
||||
1,
|
||||
0
|
||||
],
|
||||
[
|
||||
"RpmFraction",
|
||||
0.05,
|
||||
0.5,
|
||||
"Volume",
|
||||
0,
|
||||
1.1
|
||||
]
|
||||
]
|
||||
},
|
||||
"throttle_low": {
|
||||
"AudioPath": "sounds/streams/v8_c63/dec_3951_adpcm.vsnd",
|
||||
"UseRedline": true,
|
||||
"Controllers": [
|
||||
[
|
||||
"RpmFraction",
|
||||
0,
|
||||
1,
|
||||
"Pitch",
|
||||
0.74,
|
||||
1.3
|
||||
],
|
||||
[
|
||||
"Throttle",
|
||||
0,
|
||||
1,
|
||||
"Volume",
|
||||
1,
|
||||
0.1
|
||||
],
|
||||
[
|
||||
"RpmFraction",
|
||||
0,
|
||||
0.25,
|
||||
"Volume",
|
||||
0,
|
||||
0.8
|
||||
]
|
||||
]
|
||||
},
|
||||
"low": {
|
||||
"AudioPath": "sounds/streams/v8_c63/acc_2197_adpcm.vsnd",
|
||||
"UseRedline": true,
|
||||
"Controllers": [
|
||||
[
|
||||
"Throttle",
|
||||
0,
|
||||
1,
|
||||
"Volume",
|
||||
0,
|
||||
1.05
|
||||
],
|
||||
[
|
||||
"RpmFraction",
|
||||
0,
|
||||
1,
|
||||
"Pitch",
|
||||
0.9,
|
||||
1.59
|
||||
],
|
||||
[
|
||||
"RpmFraction",
|
||||
0.2,
|
||||
0.75,
|
||||
"Volume",
|
||||
1,
|
||||
0
|
||||
]
|
||||
]
|
||||
},
|
||||
"idle": {
|
||||
"AudioPath": "sounds/streams/v8_c63/acc_1560_adpcm.vsnd",
|
||||
"UseRedline": true,
|
||||
"Controllers": [
|
||||
[
|
||||
"Throttle",
|
||||
0,
|
||||
1,
|
||||
"Volume",
|
||||
1,
|
||||
0
|
||||
],
|
||||
[
|
||||
"RpmFraction",
|
||||
0,
|
||||
0.4,
|
||||
"Volume",
|
||||
1.2,
|
||||
0
|
||||
],
|
||||
[
|
||||
"RpmFraction",
|
||||
0,
|
||||
0.4,
|
||||
"Pitch",
|
||||
1,
|
||||
2
|
||||
]
|
||||
]
|
||||
},
|
||||
"high": {
|
||||
"AudioPath": "sounds/streams/v8_c63/acc_6803_adpcm.vsnd",
|
||||
"UseRedline": true,
|
||||
"Controllers": [
|
||||
[
|
||||
"Throttle",
|
||||
0,
|
||||
1,
|
||||
"Volume",
|
||||
0.35,
|
||||
1
|
||||
],
|
||||
[
|
||||
"RpmFraction",
|
||||
0.5,
|
||||
1,
|
||||
"Volume",
|
||||
0,
|
||||
1.15
|
||||
],
|
||||
[
|
||||
"RpmFraction",
|
||||
0,
|
||||
1,
|
||||
"Pitch",
|
||||
0.57,
|
||||
1
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"Paused": false,
|
||||
"__references": [],
|
||||
"__version": 0
|
||||
}
|
||||
54
Assets/sounds/suspension/compress_heavy.sound
Normal file
54
Assets/sounds/suspension/compress_heavy.sound
Normal file
@ -0,0 +1,54 @@
|
||||
{
|
||||
"UI": false,
|
||||
"Volume": "1",
|
||||
"Pitch": "1 1.2",
|
||||
"Decibels": 70,
|
||||
"SelectionMode": "Random",
|
||||
"Sounds": [
|
||||
"sounds/suspension/compress_heavy_3.vsnd",
|
||||
"sounds/suspension/compress_heavy_2.vsnd",
|
||||
"sounds/suspension/compress_heavy_1.vsnd"
|
||||
],
|
||||
"Occlusion": false,
|
||||
"AirAbsorption": true,
|
||||
"Transmission": true,
|
||||
"OcclusionRadius": 64,
|
||||
"DistanceAttenuation": true,
|
||||
"Distance": 8500,
|
||||
"Falloff": [
|
||||
{
|
||||
"x": 0,
|
||||
"y": 1,
|
||||
"in": 0,
|
||||
"out": -1.8,
|
||||
"mode": "Mirrored"
|
||||
},
|
||||
{
|
||||
"x": 0.05,
|
||||
"y": 0.22,
|
||||
"in": 3.5,
|
||||
"out": -3.5,
|
||||
"mode": "Mirrored"
|
||||
},
|
||||
{
|
||||
"x": 0.2,
|
||||
"y": 0.04,
|
||||
"in": 0.16,
|
||||
"out": -0.16,
|
||||
"mode": "Mirrored"
|
||||
},
|
||||
{
|
||||
"x": 1,
|
||||
"y": 0,
|
||||
"in": 0,
|
||||
"out": 0,
|
||||
"mode": "Mirrored"
|
||||
}
|
||||
],
|
||||
"DefaultMixer": {
|
||||
"Name": "game",
|
||||
"Id": "37db45ef-34ec-4948-b1d7-4509bc8e97e2"
|
||||
},
|
||||
"__references": [],
|
||||
"__version": 1
|
||||
}
|
||||
BIN
Assets/sounds/suspension/compress_heavy_1.wav
Normal file
BIN
Assets/sounds/suspension/compress_heavy_1.wav
Normal file
Binary file not shown.
BIN
Assets/sounds/suspension/compress_heavy_2.wav
Normal file
BIN
Assets/sounds/suspension/compress_heavy_2.wav
Normal file
Binary file not shown.
BIN
Assets/sounds/suspension/compress_heavy_3.wav
Normal file
BIN
Assets/sounds/suspension/compress_heavy_3.wav
Normal file
Binary file not shown.
55
Assets/sounds/suspension/compress_truck.sound
Normal file
55
Assets/sounds/suspension/compress_truck.sound
Normal file
@ -0,0 +1,55 @@
|
||||
{
|
||||
"UI": false,
|
||||
"Volume": "1",
|
||||
"Pitch": "1",
|
||||
"Decibels": 70,
|
||||
"SelectionMode": "Random",
|
||||
"Sounds": [
|
||||
"sounds/suspension/compress_truck_4.vsnd",
|
||||
"sounds/suspension/compress_truck_3.vsnd",
|
||||
"sounds/suspension/compress_truck_2.vsnd",
|
||||
"sounds/suspension/compress_truck_1.vsnd"
|
||||
],
|
||||
"Occlusion": true,
|
||||
"AirAbsorption": true,
|
||||
"Transmission": true,
|
||||
"OcclusionRadius": 64,
|
||||
"DistanceAttenuation": true,
|
||||
"Distance": 15000,
|
||||
"Falloff": [
|
||||
{
|
||||
"x": 0,
|
||||
"y": 1,
|
||||
"in": 0,
|
||||
"out": -1.8,
|
||||
"mode": "Mirrored"
|
||||
},
|
||||
{
|
||||
"x": 0.05,
|
||||
"y": 0.22,
|
||||
"in": 3.5,
|
||||
"out": -3.5,
|
||||
"mode": "Mirrored"
|
||||
},
|
||||
{
|
||||
"x": 0.2,
|
||||
"y": 0.04,
|
||||
"in": 0.16,
|
||||
"out": -0.16,
|
||||
"mode": "Mirrored"
|
||||
},
|
||||
{
|
||||
"x": 1,
|
||||
"y": 0,
|
||||
"in": 0,
|
||||
"out": 0,
|
||||
"mode": "Mirrored"
|
||||
}
|
||||
],
|
||||
"DefaultMixer": {
|
||||
"Name": "unknown",
|
||||
"Id": "00000000-0000-0000-0000-000000000000"
|
||||
},
|
||||
"__references": [],
|
||||
"__version": 1
|
||||
}
|
||||
BIN
Assets/sounds/suspension/compress_truck_1.wav
Normal file
BIN
Assets/sounds/suspension/compress_truck_1.wav
Normal file
Binary file not shown.
BIN
Assets/sounds/suspension/compress_truck_2.wav
Normal file
BIN
Assets/sounds/suspension/compress_truck_2.wav
Normal file
Binary file not shown.
BIN
Assets/sounds/suspension/compress_truck_3.wav
Normal file
BIN
Assets/sounds/suspension/compress_truck_3.wav
Normal file
Binary file not shown.
BIN
Assets/sounds/suspension/compress_truck_4.wav
Normal file
BIN
Assets/sounds/suspension/compress_truck_4.wav
Normal file
Binary file not shown.
54
Assets/sounds/suspension/pneumatic_down.sound
Normal file
54
Assets/sounds/suspension/pneumatic_down.sound
Normal file
@ -0,0 +1,54 @@
|
||||
{
|
||||
"UI": false,
|
||||
"Volume": "1",
|
||||
"Pitch": "1 1.2",
|
||||
"Decibels": 70,
|
||||
"SelectionMode": "Random",
|
||||
"Sounds": [
|
||||
"sounds/suspension/pneumatic_down_3.vsnd",
|
||||
"sounds/suspension/pneumatic_down_2.vsnd",
|
||||
"sounds/suspension/pneumatic_down_1.vsnd"
|
||||
],
|
||||
"Occlusion": true,
|
||||
"AirAbsorption": true,
|
||||
"Transmission": true,
|
||||
"OcclusionRadius": 64,
|
||||
"DistanceAttenuation": true,
|
||||
"Distance": 7000,
|
||||
"Falloff": [
|
||||
{
|
||||
"x": 0,
|
||||
"y": 1,
|
||||
"in": 0,
|
||||
"out": -1.8,
|
||||
"mode": "Mirrored"
|
||||
},
|
||||
{
|
||||
"x": 0.05,
|
||||
"y": 0.22,
|
||||
"in": 3.5,
|
||||
"out": -3.5,
|
||||
"mode": "Mirrored"
|
||||
},
|
||||
{
|
||||
"x": 0.2,
|
||||
"y": 0.04,
|
||||
"in": 0.16,
|
||||
"out": -0.16,
|
||||
"mode": "Mirrored"
|
||||
},
|
||||
{
|
||||
"x": 1,
|
||||
"y": 0,
|
||||
"in": 0,
|
||||
"out": 0,
|
||||
"mode": "Mirrored"
|
||||
}
|
||||
],
|
||||
"DefaultMixer": {
|
||||
"Name": "game",
|
||||
"Id": "37db45ef-34ec-4948-b1d7-4509bc8e97e2"
|
||||
},
|
||||
"__references": [],
|
||||
"__version": 1
|
||||
}
|
||||
BIN
Assets/sounds/suspension/pneumatic_down_1.wav
Normal file
BIN
Assets/sounds/suspension/pneumatic_down_1.wav
Normal file
Binary file not shown.
BIN
Assets/sounds/suspension/pneumatic_down_2.wav
Normal file
BIN
Assets/sounds/suspension/pneumatic_down_2.wav
Normal file
Binary file not shown.
BIN
Assets/sounds/suspension/pneumatic_down_3.wav
Normal file
BIN
Assets/sounds/suspension/pneumatic_down_3.wav
Normal file
Binary file not shown.
54
Assets/sounds/suspension/pneumatic_up.sound
Normal file
54
Assets/sounds/suspension/pneumatic_up.sound
Normal file
@ -0,0 +1,54 @@
|
||||
{
|
||||
"UI": false,
|
||||
"Volume": "1",
|
||||
"Pitch": "1 1.2",
|
||||
"Decibels": 70,
|
||||
"SelectionMode": "Random",
|
||||
"Sounds": [
|
||||
"sounds/suspension/pneumatic_up_3.vsnd",
|
||||
"sounds/suspension/pneumatic_up_2.vsnd",
|
||||
"sounds/suspension/pneumatic_up_1.vsnd"
|
||||
],
|
||||
"Occlusion": true,
|
||||
"AirAbsorption": true,
|
||||
"Transmission": true,
|
||||
"OcclusionRadius": 64,
|
||||
"DistanceAttenuation": true,
|
||||
"Distance": 7000,
|
||||
"Falloff": [
|
||||
{
|
||||
"x": 0,
|
||||
"y": 1,
|
||||
"in": 0,
|
||||
"out": -1.8,
|
||||
"mode": "Mirrored"
|
||||
},
|
||||
{
|
||||
"x": 0.05,
|
||||
"y": 0.22,
|
||||
"in": 3.5,
|
||||
"out": -3.5,
|
||||
"mode": "Mirrored"
|
||||
},
|
||||
{
|
||||
"x": 0.2,
|
||||
"y": 0.04,
|
||||
"in": 0.16,
|
||||
"out": -0.16,
|
||||
"mode": "Mirrored"
|
||||
},
|
||||
{
|
||||
"x": 1,
|
||||
"y": 0,
|
||||
"in": 0,
|
||||
"out": 0,
|
||||
"mode": "Mirrored"
|
||||
}
|
||||
],
|
||||
"DefaultMixer": {
|
||||
"Name": "game",
|
||||
"Id": "37db45ef-34ec-4948-b1d7-4509bc8e97e2"
|
||||
},
|
||||
"__references": [],
|
||||
"__version": 1
|
||||
}
|
||||
BIN
Assets/sounds/suspension/pneumatic_up_1.wav
Normal file
BIN
Assets/sounds/suspension/pneumatic_up_1.wav
Normal file
Binary file not shown.
BIN
Assets/sounds/suspension/pneumatic_up_2.wav
Normal file
BIN
Assets/sounds/suspension/pneumatic_up_2.wav
Normal file
Binary file not shown.
BIN
Assets/sounds/suspension/pneumatic_up_3.wav
Normal file
BIN
Assets/sounds/suspension/pneumatic_up_3.wav
Normal file
Binary file not shown.
54
Assets/sounds/suspension/stress.sound
Normal file
54
Assets/sounds/suspension/stress.sound
Normal file
@ -0,0 +1,54 @@
|
||||
{
|
||||
"UI": false,
|
||||
"Volume": "1",
|
||||
"Pitch": "1 1.1",
|
||||
"Decibels": 70,
|
||||
"SelectionMode": "Random",
|
||||
"Sounds": [
|
||||
"sounds/suspension/stress_3.vsnd",
|
||||
"sounds/suspension/stress_2.vsnd",
|
||||
"sounds/suspension/stress_1.vsnd"
|
||||
],
|
||||
"Occlusion": true,
|
||||
"AirAbsorption": true,
|
||||
"Transmission": true,
|
||||
"OcclusionRadius": 64,
|
||||
"DistanceAttenuation": true,
|
||||
"Distance": 8500,
|
||||
"Falloff": [
|
||||
{
|
||||
"x": 0,
|
||||
"y": 1,
|
||||
"in": 0,
|
||||
"out": -1.8,
|
||||
"mode": "Mirrored"
|
||||
},
|
||||
{
|
||||
"x": 0.05,
|
||||
"y": 0.22,
|
||||
"in": 3.5,
|
||||
"out": -3.5,
|
||||
"mode": "Mirrored"
|
||||
},
|
||||
{
|
||||
"x": 0.2,
|
||||
"y": 0.04,
|
||||
"in": 0.16,
|
||||
"out": -0.16,
|
||||
"mode": "Mirrored"
|
||||
},
|
||||
{
|
||||
"x": 1,
|
||||
"y": 0,
|
||||
"in": 0,
|
||||
"out": 0,
|
||||
"mode": "Mirrored"
|
||||
}
|
||||
],
|
||||
"DefaultMixer": {
|
||||
"Name": "game",
|
||||
"Id": "37db45ef-34ec-4948-b1d7-4509bc8e97e2"
|
||||
},
|
||||
"__references": [],
|
||||
"__version": 1
|
||||
}
|
||||
BIN
Assets/sounds/suspension/stress_1.wav
Normal file
BIN
Assets/sounds/suspension/stress_1.wav
Normal file
Binary file not shown.
BIN
Assets/sounds/suspension/stress_2.wav
Normal file
BIN
Assets/sounds/suspension/stress_2.wav
Normal file
Binary file not shown.
BIN
Assets/sounds/suspension/stress_3.wav
Normal file
BIN
Assets/sounds/suspension/stress_3.wav
Normal file
Binary file not shown.
11
Code/Base/VeloXBase.Input.cs
Normal file
11
Code/Base/VeloXBase.Input.cs
Normal file
@ -0,0 +1,11 @@
|
||||
using Sandbox;
|
||||
|
||||
namespace VeloX;
|
||||
|
||||
public abstract partial class VeloXBase
|
||||
{
|
||||
//[Property, Feature( "Input" ), InputAction] string ThrottleInput { get; set; } = "Forward";
|
||||
[Property, Feature( "Input" )] internal InputResolver Input { get; set; } = new();
|
||||
[Property, Feature( "Input" )] public GameObject Driver { get => Input.Driver; set => Input.Driver = value; }
|
||||
|
||||
}
|
||||
43
Code/Base/VeloXBase.Phys.cs
Normal file
43
Code/Base/VeloXBase.Phys.cs
Normal file
@ -0,0 +1,43 @@
|
||||
using Sandbox;
|
||||
|
||||
namespace VeloX;
|
||||
|
||||
public abstract partial class VeloXBase
|
||||
{
|
||||
|
||||
private Vector3 linForce;
|
||||
private Vector3 angForce;
|
||||
|
||||
private void PhysicsSimulate()
|
||||
{
|
||||
var drag = AngularDrag;
|
||||
var mass = Body.Mass;
|
||||
var angVel = Body.AngularVelocity;
|
||||
|
||||
linForce.x = 0;
|
||||
linForce.y = 0;
|
||||
linForce.z = 0;
|
||||
|
||||
angForce.x = angVel.x * drag.x * mass;
|
||||
angForce.y = angVel.y * drag.y * mass;
|
||||
angForce.z = angVel.z * drag.z * mass;
|
||||
|
||||
|
||||
if ( Wheels.Count > 0 )
|
||||
{
|
||||
Vector3 vehVel = Body.Velocity;
|
||||
Vector3 vehAngVel = Body.AngularVelocity;
|
||||
|
||||
var dt = Time.Delta;
|
||||
foreach ( var v in Wheels )
|
||||
v.DoPhysics( this, ref vehVel, ref vehAngVel, ref linForce, ref angForce, in dt );
|
||||
|
||||
Body.Velocity = vehVel;
|
||||
Body.AngularVelocity = vehAngVel;
|
||||
}
|
||||
|
||||
|
||||
Body.ApplyForce( linForce );
|
||||
Body.ApplyTorque( angForce );
|
||||
}
|
||||
}
|
||||
62
Code/Base/VeloXBase.Sound.cs
Normal file
62
Code/Base/VeloXBase.Sound.cs
Normal file
@ -0,0 +1,62 @@
|
||||
using Sandbox;
|
||||
using Sandbox.Audio;
|
||||
using System;
|
||||
using System.Text.Json.Serialization;
|
||||
using VeloX.Audio;
|
||||
using static Sandbox.Utility.Noise;
|
||||
using static VeloX.EngineStream;
|
||||
|
||||
namespace VeloX;
|
||||
|
||||
|
||||
public abstract partial class VeloXBase : Component, Component.ICollisionListener
|
||||
{
|
||||
|
||||
[Property, Feature( "Effects" ), Group( "Sound" )]
|
||||
public SoundEvent SuspensionHeavySound { get; protected set; } = ResourceLibrary.Get<SoundEvent>( "sounds/suspension/compress_heavy.sound" );
|
||||
|
||||
[Property, Feature( "Effects" ), Group( "Sound" )]
|
||||
public SoundEvent SuspensionDownSound { get; protected set; } = ResourceLibrary.Get<SoundEvent>( "sounds/suspension/pneumatic_down.sound" );
|
||||
|
||||
[Property, Feature( "Effects" ), Group( "Sound" )]
|
||||
public SoundEvent SuspensionUpSound { get; protected set; } = ResourceLibrary.Get<SoundEvent>( "sounds/suspension/pneumatic_up.sound" );
|
||||
|
||||
[Property, Feature( "Effects" ), Group( "Sound" )]
|
||||
public SoundEvent SoftCollisionSound { get; protected set; } = ResourceLibrary.Get<SoundEvent>( "sounds/collisions/car_light.sound" );
|
||||
|
||||
[Property, Feature( "Effects" ), Group( "Sound" )]
|
||||
public SoundEvent HardCollisionSound { get; protected set; } = ResourceLibrary.Get<SoundEvent>( "sounds/collisions/car_heavy.sound" );
|
||||
|
||||
[Property, Feature( "Effects" ), Group( "Sound" )]
|
||||
public SoundEvent VehicleScrapeSound { get; protected set; } = ResourceLibrary.Get<SoundEvent>( "sounds/collisions/metal_scrape.sound" );
|
||||
|
||||
[Property, Feature( "Effects" ), Group( "Particle" )]
|
||||
public GameObject MetalImpactEffect { get; protected set; } = GameObject.GetPrefab( "effects/metal_impact.prefab" );
|
||||
void ICollisionListener.OnCollisionStart( Collision collision )
|
||||
{
|
||||
|
||||
var speed = MathF.Abs( collision.Contact.NormalSpeed );
|
||||
var surfaceNormal = collision.Contact.Normal;
|
||||
if ( speed < 100 )
|
||||
return;
|
||||
|
||||
var isHardHit = speed > 300;
|
||||
var volume = Math.Clamp( speed / 400f, 0, 1 );
|
||||
var sound = Sound.Play( SoftCollisionSound, WorldPosition );
|
||||
sound.Volume = volume;
|
||||
|
||||
var a = MetalImpactEffect.Clone( new CloneConfig() { StartEnabled = true, Transform = new( collision.Contact.Point, surfaceNormal.Cross( surfaceNormal ).EulerAngles ) } );
|
||||
a.Components.Get<ParticleConeEmitter>().Burst = speed / 2;
|
||||
|
||||
if ( isHardHit )
|
||||
{
|
||||
var hardSound = Sound.Play( HardCollisionSound, WorldPosition );
|
||||
hardSound.Volume = volume;
|
||||
}
|
||||
else if ( surfaceNormal.Dot( -collision.Contact.Speed.Normal ) < 0.5f )
|
||||
{
|
||||
var scrapSound = Sound.Play( VehicleScrapeSound, WorldPosition );
|
||||
scrapSound.Volume = 0.4f;
|
||||
}
|
||||
}
|
||||
}
|
||||
20
Code/Base/VeloXBase.Wheel.cs
Normal file
20
Code/Base/VeloXBase.Wheel.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using Sandbox;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace VeloX;
|
||||
|
||||
public abstract partial class VeloXBase
|
||||
{
|
||||
|
||||
[Property] public List<VeloXWheel> Wheels { get; set; }
|
||||
[Property] public TagSet WheelIgnoredTags { get; set; }
|
||||
|
||||
public List<VeloXWheel> FindWheels() => [.. Components.GetAll<VeloXWheel>()];
|
||||
|
||||
[Button]
|
||||
public void SetupWheels()
|
||||
{
|
||||
Wheels = FindWheels();
|
||||
}
|
||||
|
||||
}
|
||||
38
Code/Base/VeloXBase.cs
Normal file
38
Code/Base/VeloXBase.cs
Normal file
@ -0,0 +1,38 @@
|
||||
using Sandbox;
|
||||
|
||||
namespace VeloX;
|
||||
|
||||
|
||||
public abstract partial class VeloXBase : Component
|
||||
{
|
||||
[Sync] public EngineState EngineState { get; set; }
|
||||
[Sync] public WaterState WaterState { get; set; }
|
||||
[Sync] public bool IsEngineOnFire { get; set; }
|
||||
[Sync, Range( 0, 1 ), Property] public float Brake { get; set; }
|
||||
[Sync( SyncFlags.Interpolate ), Range( 0, 1 ), Property] public float Throttle { get; set; }
|
||||
|
||||
[Property] public Vector3 AngularDrag { get; set; } = new( -0.1f, -0.1f, -3 );
|
||||
[Property] public float Mass { get; set; } = 900;
|
||||
[Property, Group( "Components" )] public Rigidbody Body { get; protected set; }
|
||||
[Property, Group( "Components" )] public Collider Collider { get; protected set; }
|
||||
|
||||
[Sync] public Angles SteerAngle { get; set; }
|
||||
|
||||
public Vector3 LocalVelocity;
|
||||
public float ForwardSpeed;
|
||||
public float TotalSpeed;
|
||||
|
||||
protected override void OnFixedUpdate()
|
||||
{
|
||||
if ( IsProxy )
|
||||
return;
|
||||
|
||||
LocalVelocity = WorldTransform.PointToLocal( WorldPosition + Body.Velocity );
|
||||
ForwardSpeed = LocalVelocity.x;
|
||||
TotalSpeed = LocalVelocity.Length;
|
||||
Body.PhysicsBody.Mass = Mass;
|
||||
|
||||
PhysicsSimulate();
|
||||
}
|
||||
|
||||
}
|
||||
74
Code/Base/Wheel/VeloXWheel.Gizmo.cs
Normal file
74
Code/Base/Wheel/VeloXWheel.Gizmo.cs
Normal file
@ -0,0 +1,74 @@
|
||||
using Sandbox;
|
||||
|
||||
namespace VeloX;
|
||||
|
||||
|
||||
public partial class VeloXWheel : Component
|
||||
{
|
||||
protected override void DrawGizmos()
|
||||
{
|
||||
|
||||
if ( !Gizmo.IsSelected )
|
||||
return;
|
||||
|
||||
Gizmo.Draw.IgnoreDepth = true;
|
||||
|
||||
//
|
||||
// Suspension length
|
||||
//
|
||||
{
|
||||
var suspensionStart = Vector3.Zero;
|
||||
var suspensionEnd = Vector3.Zero + Vector3.Down * SuspensionLength;
|
||||
|
||||
Gizmo.Draw.Color = Color.Cyan;
|
||||
Gizmo.Draw.LineThickness = 0.25f;
|
||||
|
||||
Gizmo.Draw.Line( suspensionStart, suspensionEnd );
|
||||
|
||||
Gizmo.Draw.Line( suspensionStart + Vector3.Forward, suspensionStart + Vector3.Backward );
|
||||
Gizmo.Draw.Line( suspensionEnd + Vector3.Forward, suspensionEnd + Vector3.Backward );
|
||||
}
|
||||
var widthOffset = Vector3.Right * Width * 0.5f;
|
||||
//
|
||||
// Wheel radius
|
||||
//
|
||||
{
|
||||
Gizmo.Draw.LineThickness = 0.5f;
|
||||
Gizmo.Draw.Color = Color.White;
|
||||
|
||||
Gizmo.Draw.LineCylinder( widthOffset, -widthOffset, Radius, Radius, 16 );
|
||||
}
|
||||
|
||||
//
|
||||
// Wheel width
|
||||
//
|
||||
{
|
||||
var circlePosition = Vector3.Zero;
|
||||
|
||||
Gizmo.Draw.LineThickness = 0.25f;
|
||||
Gizmo.Draw.Color = Color.White;
|
||||
|
||||
for ( float i = 0; i < 16; i++ )
|
||||
{
|
||||
|
||||
var pos = circlePosition + Vector3.Up.RotateAround( Vector3.Zero, new Angles( i / 16 * 360, 0, 0 ) ) * Radius;
|
||||
|
||||
Gizmo.Draw.Line( new Line( pos - widthOffset, pos + widthOffset ) );
|
||||
|
||||
var pos2 = circlePosition + Vector3.Up.RotateAround( Vector3.Zero, new Angles( (i + 1) / 16 * 360, 0, 0 ) ) * Radius;
|
||||
Gizmo.Draw.Line( pos - widthOffset, pos2 + widthOffset );
|
||||
}
|
||||
}
|
||||
|
||||
////
|
||||
//// Forward direction
|
||||
////
|
||||
//{
|
||||
// var arrowStart = Vector3.Forward * Radius;
|
||||
// var arrowEnd = arrowStart + Vector3.Forward * 8f;
|
||||
|
||||
// Gizmo.Draw.Color = Color.Red;
|
||||
// Gizmo.Draw.Arrow( arrowStart, arrowEnd, 4, 1 );
|
||||
//}
|
||||
}
|
||||
}
|
||||
18
Code/Base/Wheel/VeloXWheel.Suspension.cs
Normal file
18
Code/Base/Wheel/VeloXWheel.Suspension.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using Sandbox;
|
||||
|
||||
namespace VeloX;
|
||||
|
||||
public partial class VeloXWheel
|
||||
{
|
||||
[Property] float BrakePowerMax { get; set; } = 3000;
|
||||
[Property, Group( "Suspension" )] float SuspensionLength { get; set; } = 10;
|
||||
[Property, Group( "Suspension" )] float SpringStrength { get; set; } = 800;
|
||||
[Property, Group( "Suspension" )] float SpringDamper { get; set; } = 3000;
|
||||
|
||||
[Property, Group( "Traction" )] float ForwardTractionMax { get; set; } = 2600;
|
||||
[Property, Group( "Traction" )] float SideTractionMultiplier { get; set; } = 20;
|
||||
[Property, Group( "Traction" )] float SideTractionMaxAng { get; set; } = 25;
|
||||
[Property, Group( "Traction" )] float SideTractionMax { get; set; } = 2400;
|
||||
[Property, Group( "Traction" )] float SideTractionMin { get; set; } = 800;
|
||||
|
||||
}
|
||||
240
Code/Base/Wheel/VeloXWheel.cs
Normal file
240
Code/Base/Wheel/VeloXWheel.cs
Normal file
@ -0,0 +1,240 @@
|
||||
using Sandbox;
|
||||
using System;
|
||||
using System.Runtime.Intrinsics.Arm;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace VeloX;
|
||||
|
||||
[Group( "VeloX" )]
|
||||
[Title( "VeloX - Wheel" )]
|
||||
public partial class VeloXWheel : Component
|
||||
{
|
||||
|
||||
[Property] public float Radius { get; set; } = 15;
|
||||
[Property] public float Width { get; set; } = 6;
|
||||
[Sync] public float SideSlip { get; private set; }
|
||||
[Sync] public float ForwardSlip { get; private set; }
|
||||
[Sync, Range( 0, 1 )] public float BrakePower { get; set; }
|
||||
[Sync] public float Torque { get; set; }
|
||||
[Sync, Range( 0, 1 )] public float Brake { get; set; }
|
||||
[Property] public bool IsFront { get; protected set; }
|
||||
[Property] public float SteerMultiplier { get; set; }
|
||||
public float Spin { get; private set; }
|
||||
|
||||
public float RPM { get => angularVelocity * 60f / MathF.Tau; set => angularVelocity = value / (60 / MathF.Tau); }
|
||||
internal float DistributionFactor { get; set; }
|
||||
|
||||
private Vector3 StartPos { get; set; }
|
||||
private static Rotation CylinderOffset => Rotation.FromRoll( 90 );
|
||||
|
||||
public SceneTraceResult Trace { get; private set; }
|
||||
public bool IsOnGround => Trace.Hit;
|
||||
|
||||
private float lastSpringOffset;
|
||||
private Vector2 tractionCycle;
|
||||
private float angularVelocity;
|
||||
private float lastFraction;
|
||||
private RealTimeUntil expandSoundCD;
|
||||
private RealTimeUntil contractSoundCD;
|
||||
|
||||
|
||||
protected override void OnAwake()
|
||||
{
|
||||
base.OnAwake();
|
||||
if ( StartPos.IsNearZeroLength )
|
||||
StartPos = LocalPosition;
|
||||
}
|
||||
|
||||
private static float TractionRamp( float slipAngle, float sideTractionMaxAng, float sideTractionMax, float sideTractionMin )
|
||||
{
|
||||
sideTractionMaxAng /= 90; // Convert max slip angle to the 0 - 1 range
|
||||
var x = (slipAngle - sideTractionMaxAng) / (1 - sideTractionMaxAng);
|
||||
|
||||
|
||||
return slipAngle < sideTractionMaxAng ? sideTractionMax : (sideTractionMax * (1 - x)) + (sideTractionMin * x);
|
||||
}
|
||||
|
||||
private void DoSuspensionSounds( VeloXBase vehicle, float change )
|
||||
{
|
||||
if ( change > 0.1f && expandSoundCD )
|
||||
{
|
||||
expandSoundCD = 0.3f;
|
||||
var sound = Sound.Play( vehicle.SuspensionUpSound, WorldPosition );
|
||||
sound.Volume = Math.Clamp( Math.Abs( change ) * 5f, 0, 0.5f );
|
||||
}
|
||||
|
||||
if ( change < -0.1f && contractSoundCD )
|
||||
{
|
||||
contractSoundCD = 0.3f;
|
||||
change = MathF.Abs( change );
|
||||
var sound = Sound.Play( change > 0.3f ? vehicle.SuspensionHeavySound : vehicle.SuspensionDownSound, WorldPosition );
|
||||
sound.Volume = Math.Clamp( change * 5f, 0, 1 );
|
||||
}
|
||||
}
|
||||
|
||||
internal void Update( VeloXBase vehicle, in float dt )
|
||||
{
|
||||
UpdateVisuals( vehicle, dt );
|
||||
|
||||
if ( !IsOnGround )
|
||||
{
|
||||
angularVelocity += (Torque / 20) * dt;
|
||||
angularVelocity = MathX.Approach( angularVelocity, 0, dt * 4 );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void UpdateVisuals( VeloXBase vehicle, in float dt )
|
||||
{
|
||||
//Rotate the wheel around the axle axis
|
||||
Spin = (Spin - MathX.RadianToDegree( angularVelocity ) * dt) % 360;
|
||||
|
||||
WorldRotation = vehicle.WorldTransform.RotationToWorld( vehicle.SteerAngle * SteerMultiplier ).RotateAroundAxis( Vector3.Right, Spin );
|
||||
|
||||
}
|
||||
|
||||
public void DoPhysics( VeloXBase vehicle, ref Vector3 vehVel, ref Vector3 vehAngVel, ref Vector3 outLin, ref Vector3 outAng, in float dt )
|
||||
{
|
||||
|
||||
var pos = vehicle.WorldTransform.PointToWorld( StartPos );
|
||||
|
||||
var ang = vehicle.WorldTransform.RotationToWorld( vehicle.SteerAngle * SteerMultiplier );
|
||||
|
||||
var fw = ang.Forward;
|
||||
var rt = ang.Right;
|
||||
var up = ang.Up;
|
||||
|
||||
var maxLen = SuspensionLength;
|
||||
|
||||
var endPos = pos + ang.Down * maxLen;
|
||||
Trace = Scene.Trace.IgnoreGameObjectHierarchy( vehicle.GameObject )
|
||||
.FromTo( pos, endPos )
|
||||
.Cylinder( Width, Radius )
|
||||
.Rotated( vehicle.WorldTransform.Rotation * CylinderOffset )
|
||||
.UseRenderMeshes( false )
|
||||
.UseHitPosition( false )
|
||||
.WithoutTags( vehicle.WheelIgnoredTags )
|
||||
.Run();
|
||||
|
||||
var fraction = Trace.Fraction;
|
||||
|
||||
var contactPos = pos - maxLen * fraction * up;
|
||||
|
||||
LocalPosition = vehicle.WorldTransform.PointToLocal( contactPos );
|
||||
|
||||
DoSuspensionSounds( vehicle, fraction - lastFraction );
|
||||
lastFraction = fraction;
|
||||
|
||||
if ( !IsOnGround )
|
||||
{
|
||||
SideSlip = 0;
|
||||
ForwardSlip = 0;
|
||||
return;
|
||||
}
|
||||
var normal = Trace.Normal;
|
||||
|
||||
var vel = vehicle.Body.GetVelocityAtPoint( pos );
|
||||
// Split that velocity among our local directions
|
||||
|
||||
var velF = fw.Dot( vel );
|
||||
|
||||
var velR = rt.Dot( vel );
|
||||
|
||||
var velU = normal.Dot( vel );
|
||||
|
||||
var absVelR = Math.Abs( velR );
|
||||
|
||||
|
||||
//Make forward forces be perpendicular to the surface normal
|
||||
|
||||
fw = normal.Cross( rt );
|
||||
|
||||
//Suspension spring force &damping
|
||||
|
||||
var offset = maxLen - (fraction * maxLen);
|
||||
|
||||
var springForce = (offset * SpringStrength);
|
||||
|
||||
var damperForce = (lastSpringOffset - offset) * SpringDamper;
|
||||
lastSpringOffset = offset;
|
||||
var force = (springForce - damperForce) * up.Dot( normal ) * normal;
|
||||
|
||||
//If the suspension spring is going to be fully compressed on the next frame...
|
||||
|
||||
if ( velU < 0 && offset + Math.Abs( velU * dt ) > SuspensionLength )
|
||||
{
|
||||
var (linearVel, angularVel) = vehicle.Body.PhysicsBody.CalculateVelocityOffset( (-velU / dt) * normal, pos );
|
||||
vehVel += linearVel;
|
||||
vehAngVel += angularVel;
|
||||
}
|
||||
|
||||
//Rolling resistance
|
||||
force += 0.05f * -velF * fw;
|
||||
|
||||
//Brake and torque forces
|
||||
var surfaceGrip = 1;
|
||||
|
||||
var maxTraction = ForwardTractionMax * surfaceGrip * 1;
|
||||
//Grip loss logic
|
||||
|
||||
var brakeForce = MathX.Clamp( -velF, -Brake, Brake ) * BrakePowerMax * surfaceGrip;
|
||||
|
||||
var forwardForce = Torque + brakeForce;
|
||||
|
||||
var signForwardForce = forwardForce > 0 ? 1 : (forwardForce < 0 ? -1 : 0);
|
||||
|
||||
// Given an amount of sideways slippage( up to the max.traction )
|
||||
// and the forward force, calculate how much grip we are losing.
|
||||
|
||||
tractionCycle.x = Math.Min( absVelR, maxTraction );
|
||||
tractionCycle.y = forwardForce;
|
||||
|
||||
var gripLoss = Math.Max( tractionCycle.Length - maxTraction, 0 );
|
||||
|
||||
|
||||
// Reduce the forward force by the amount of grip we lost,
|
||||
|
||||
// but still allow some amount of brake force to apply regardless.
|
||||
|
||||
forwardForce += -(gripLoss * signForwardForce) + MathX.Clamp( brakeForce * 0.5f, -maxTraction, maxTraction );
|
||||
|
||||
force += fw * forwardForce;
|
||||
|
||||
// Get how fast the wheel would be spinning if it had never lost grip
|
||||
|
||||
var groundAngularVelocity = MathF.Tau * (velF / (Radius * MathF.Tau));
|
||||
|
||||
// Add our grip loss to our spin velocity
|
||||
var _angvel = groundAngularVelocity + gripLoss * (Torque > 0 ? 1 : (Torque < 0 ? -1 : 0));
|
||||
|
||||
// Smoothly match our current angular velocity to the angular velocity affected by grip loss
|
||||
|
||||
angularVelocity = MathX.Approach( angularVelocity, _angvel, dt * 200 );
|
||||
ForwardSlip = groundAngularVelocity - angularVelocity;
|
||||
|
||||
// Calculate side slip angle
|
||||
var slipAngle = MathF.Atan2( velR, MathF.Abs( velF ) ) / MathF.PI * 2;
|
||||
SideSlip = slipAngle * MathX.Clamp( vehicle.TotalSpeed * 0.005f, 0, 1 ) * 2;
|
||||
|
||||
//Sideways traction ramp
|
||||
|
||||
slipAngle = MathF.Abs( slipAngle * slipAngle );
|
||||
|
||||
maxTraction = TractionRamp( slipAngle, SideTractionMaxAng, SideTractionMax, SideTractionMin );
|
||||
|
||||
var sideForce = -rt.Dot( vel * SideTractionMultiplier );
|
||||
|
||||
// Reduce sideways traction force as the wheel slips forward
|
||||
sideForce *= 1 - Math.Clamp( MathF.Abs( gripLoss ) * 0.1f, 0, 1 ) * 0.9f;
|
||||
|
||||
// Apply sideways traction force
|
||||
force += Math.Clamp( sideForce, -maxTraction, maxTraction ) * surfaceGrip * rt;
|
||||
|
||||
force += velR * SideTractionMultiplier * -0.1f * rt;
|
||||
//Apply the forces at the axle / ground contact position
|
||||
|
||||
vehicle.Body.ApplyForceAt( pos, force / dt );
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
358
Code/Car/VeloXCar.Engine.cs
Normal file
358
Code/Car/VeloXCar.Engine.cs
Normal file
@ -0,0 +1,358 @@
|
||||
using Sandbox;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace VeloX;
|
||||
|
||||
public partial class VeloXCar
|
||||
{
|
||||
[Property, Feature( "Engine" )] public EngineStream Stream { get; set; }
|
||||
public EngineStreamPlayer StreamPlayer { get; set; }
|
||||
|
||||
[Property, Feature( "Engine" )] public float MinRPM { get; set; } = 800;
|
||||
[Property, Feature( "Engine" )] public float MaxRPM { get; set; } = 7000;
|
||||
[Property, Feature( "Engine" ), Range( -1, 1 )]
|
||||
public float PowerDistribution
|
||||
{
|
||||
get => powerDistribution; set
|
||||
{
|
||||
powerDistribution = value;
|
||||
UpdatePowerDistribution();
|
||||
}
|
||||
}
|
||||
[Property, Feature( "Engine" )] public float FlyWheelMass { get; set; } = 80f;
|
||||
[Property, Feature( "Engine" )] public float FlyWheelRadius { get; set; } = 0.5f;
|
||||
[Property, Feature( "Engine" )] public float FlywheelFriction { get; set; } = -6000;
|
||||
[Property, Feature( "Engine" )] public float FlywheelTorque { get; set; } = 20000;
|
||||
[Property, Feature( "Engine" )] public float EngineBrakeTorque { get; set; } = 2000;
|
||||
[Property, Feature( "Engine" )]
|
||||
public Dictionary<int, float> Gears { get; set; } = new()
|
||||
{
|
||||
[-1] = 2.5f,
|
||||
[0] = 0f,
|
||||
[1] = 2.8f,
|
||||
[2] = 1.7f,
|
||||
[3] = 1.2f,
|
||||
[4] = 0.9f,
|
||||
[5] = 0.75f,
|
||||
[6] = 0.7f
|
||||
};
|
||||
[Property, Feature( "Engine" )] public float DifferentialRatio { get; set; } = 1f;
|
||||
[Property, Feature( "Engine" ), Range( 0, 1 )] public float TransmissionEfficiency { get; set; } = 0.8f;
|
||||
[Property, Feature( "Engine" )] private float MinRPMTorque { get; set; } = 5000f;
|
||||
[Property, Feature( "Engine" )] private float MaxRPMTorque { get; set; } = 8000f;
|
||||
[Sync] public int Gear { get; set; } = 0;
|
||||
[Sync] public float Clutch { get; set; } = 1;
|
||||
[Sync( SyncFlags.Interpolate )] public float EngineRPM { get; set; }
|
||||
public float RPMPercent => (EngineRPM - MinRPM) / MaxRPM;
|
||||
|
||||
private const float TAU = MathF.Tau;
|
||||
|
||||
private int MinGear { get; set; }
|
||||
private int MaxGear { get; set; }
|
||||
|
||||
[Sync] public bool IsRedlining { get; private set; }
|
||||
|
||||
private float flywheelVelocity;
|
||||
private TimeUntil switchCD = 0;
|
||||
private float groundedCount;
|
||||
private float burnout;
|
||||
|
||||
private float frontBrake;
|
||||
private float rearBrake;
|
||||
|
||||
private float availableFrontTorque;
|
||||
private float availableRearTorque;
|
||||
|
||||
private float avgSideSlip;
|
||||
private float avgPoweredRPM;
|
||||
private float avgForwardSlip;
|
||||
|
||||
private float inputThrottle, inputBrake;
|
||||
private bool inputHandbrake;
|
||||
private float transmissionRPM;
|
||||
private float powerDistribution;
|
||||
|
||||
public float FlywheelRPM
|
||||
{
|
||||
get => flywheelVelocity * 60 / TAU;
|
||||
set
|
||||
{
|
||||
flywheelVelocity = value * TAU / 60; EngineRPM = value;
|
||||
}
|
||||
}
|
||||
private void UpdateGearList()
|
||||
{
|
||||
int minGear = 0;
|
||||
int maxGear = 0;
|
||||
|
||||
foreach ( var (gear, ratio) in Gears )
|
||||
{
|
||||
|
||||
if ( gear < minGear )
|
||||
minGear = gear;
|
||||
|
||||
if ( gear > maxGear )
|
||||
maxGear = gear;
|
||||
|
||||
if ( minGear != 0 || maxGear != 0 )
|
||||
{
|
||||
SwitchGear( 0, false );
|
||||
}
|
||||
}
|
||||
|
||||
MinGear = minGear;
|
||||
MaxGear = maxGear;
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void SwitchGear( int index, bool cooldown = true )
|
||||
{
|
||||
if ( Gear == index ) return;
|
||||
index = Math.Clamp( index, MinGear, MaxGear );
|
||||
|
||||
if ( index == 0 || !cooldown )
|
||||
switchCD = 0;
|
||||
else
|
||||
switchCD = 0.3f;
|
||||
Clutch = 1;
|
||||
Gear = index;
|
||||
}
|
||||
|
||||
public float TransmissionToEngineRPM( int gear ) => avgPoweredRPM * Gears[gear] * DifferentialRatio * 60 / TAU;
|
||||
public float GetTransmissionMaxRPM( int gear ) => FlywheelRPM / Gears[gear] / DifferentialRatio;
|
||||
|
||||
private void UpdatePowerDistribution()
|
||||
{
|
||||
if ( Wheels is null ) return;
|
||||
int frontCount = 0, rearCount = 0;
|
||||
|
||||
foreach ( var wheel in Wheels )
|
||||
{
|
||||
if ( wheel.IsFront )
|
||||
frontCount++;
|
||||
else
|
||||
rearCount++;
|
||||
|
||||
}
|
||||
|
||||
float frontDistribution = 0.5f + PowerDistribution * 0.5f;
|
||||
|
||||
float rearDistribution = 1 - frontDistribution;
|
||||
|
||||
frontDistribution /= frontCount;
|
||||
rearDistribution /= rearCount;
|
||||
|
||||
foreach ( var wheel in Wheels )
|
||||
if ( wheel.IsFront )
|
||||
wheel.DistributionFactor = frontDistribution;
|
||||
else
|
||||
wheel.DistributionFactor = rearDistribution;
|
||||
}
|
||||
|
||||
private void EngineAccelerate( float torque, float dt )
|
||||
{
|
||||
var inertia = 0.5f * FlyWheelMass * FlyWheelRadius * FlyWheelRadius;
|
||||
var angularAcceleration = torque / inertia;
|
||||
|
||||
flywheelVelocity += angularAcceleration * dt;
|
||||
}
|
||||
|
||||
private float GetTransmissionTorque( int gear, float minTorque, float maxTorque )
|
||||
{
|
||||
var torque = FlywheelRPM.Remap( MinRPM, MaxRPM, minTorque, maxTorque, true );
|
||||
torque *= (1 - Clutch);
|
||||
torque = torque * Gears[gear] * DifferentialRatio * TransmissionEfficiency;
|
||||
|
||||
return gear == -1 ? -torque : torque;
|
||||
}
|
||||
|
||||
private void AutoGearSwitch()
|
||||
{
|
||||
if ( ForwardSpeed < 100 && Input.Down( "Backward" ) )
|
||||
{
|
||||
SwitchGear( -1, false );
|
||||
return;
|
||||
}
|
||||
|
||||
var currentGear = Gear;
|
||||
|
||||
if ( currentGear < 0 && ForwardSpeed < -100 )
|
||||
return;
|
||||
|
||||
|
||||
if ( Math.Abs( avgForwardSlip ) > 10 )
|
||||
return;
|
||||
|
||||
var gear = Math.Clamp( currentGear, 1, MaxGear );
|
||||
|
||||
float minRPM = MinRPM, maxRPM = MaxRPM;
|
||||
|
||||
|
||||
maxRPM *= 0.98f;
|
||||
|
||||
float gearRPM;
|
||||
|
||||
|
||||
for ( int i = 1; i <= MaxGear; i++ )
|
||||
{
|
||||
gearRPM = TransmissionToEngineRPM( i );
|
||||
|
||||
if ( (i == 1 && gearRPM < minRPM) || (gearRPM > minRPM && gearRPM < maxRPM) )
|
||||
{
|
||||
gear = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var threshold = minRPM + (maxRPM - minRPM) * (0.5 - Throttle * 0.3);
|
||||
if ( gear < currentGear && gear > currentGear - 2 && EngineRPM > threshold )
|
||||
return;
|
||||
|
||||
SwitchGear( gear );
|
||||
}
|
||||
|
||||
private float EngineClutch( float dt )
|
||||
{
|
||||
if ( !switchCD )
|
||||
{
|
||||
inputThrottle = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ( inputHandbrake )
|
||||
return 1;
|
||||
|
||||
var absForwardSpeed = Math.Abs( ForwardSpeed );
|
||||
|
||||
if ( groundedCount < 1 && absForwardSpeed > 30 )
|
||||
return 1;
|
||||
|
||||
if ( ForwardSpeed < -50 && inputBrake > 0 && Gear < 0 )
|
||||
return 1;
|
||||
|
||||
if ( absForwardSpeed > 200 )
|
||||
return 0;
|
||||
|
||||
return inputThrottle > 0.1f ? 0 : 1;
|
||||
}
|
||||
|
||||
private void EngineThink( float dt )
|
||||
{
|
||||
inputThrottle = Input.Down( "Forward" ) ? 1 : 0;
|
||||
inputBrake = Input.Down( "Backward" ) ? 1 : 0;
|
||||
inputHandbrake = Input.Down( "Jump" );
|
||||
if ( burnout > 0 )
|
||||
{
|
||||
SwitchGear( 1, false );
|
||||
if ( inputThrottle < 0.1f || inputBrake < 0.1f )
|
||||
burnout = 0;
|
||||
}
|
||||
else
|
||||
AutoGearSwitch();
|
||||
|
||||
if ( Gear < 0 )
|
||||
(inputBrake, inputThrottle) = (inputThrottle, inputBrake);
|
||||
|
||||
var rpm = FlywheelRPM;
|
||||
|
||||
var clutch = EngineClutch( dt );
|
||||
|
||||
if ( inputThrottle > 0.1 && inputBrake > 0.1 && Math.Abs( ForwardSpeed ) < 50 )
|
||||
{
|
||||
burnout = MathX.Approach( burnout, 1, dt * 2 );
|
||||
Clutch = 0;
|
||||
|
||||
}
|
||||
else if ( inputHandbrake )
|
||||
{
|
||||
frontBrake = 0f;
|
||||
rearBrake = 0.5f;
|
||||
Clutch = 1;
|
||||
clutch = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( (Gear == -1 || Gear == 1) && inputThrottle < 0.05f && inputBrake < 0.1f && groundedCount > 1 && rpm < MinRPM * 1.2f )
|
||||
inputBrake = 0.2f;
|
||||
|
||||
frontBrake = inputBrake * 0.5f;
|
||||
rearBrake = inputBrake * 0.5f;
|
||||
}
|
||||
|
||||
clutch = MathX.Approach( Clutch, clutch, dt * ((Gear < 2 && inputThrottle > 0.1f) ? 6 : 2) );
|
||||
|
||||
Clutch = clutch;
|
||||
|
||||
var isRedlining = false;
|
||||
transmissionRPM = 0;
|
||||
|
||||
if ( Gear != 0 )
|
||||
{
|
||||
transmissionRPM = TransmissionToEngineRPM( Gear );
|
||||
transmissionRPM = Gear < 0 ? -transmissionRPM : transmissionRPM;
|
||||
rpm = (rpm * clutch) + (MathF.Max( 0, transmissionRPM ) * (1 - clutch));
|
||||
}
|
||||
var throttle = Throttle;
|
||||
|
||||
var gearTorque = GetTransmissionTorque( Gear, MinRPMTorque, MaxRPMTorque );
|
||||
|
||||
var availableTorque = gearTorque * throttle;
|
||||
|
||||
if ( transmissionRPM < 0 )
|
||||
{
|
||||
availableTorque += gearTorque * 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
var engineBrakeTorque = GetTransmissionTorque( Gear, EngineBrakeTorque, EngineBrakeTorque );
|
||||
|
||||
availableTorque -= engineBrakeTorque * (1 - throttle) * 0.5f;
|
||||
}
|
||||
|
||||
var maxRPM = MaxRPM;
|
||||
|
||||
if ( rpm < MinRPM )
|
||||
{
|
||||
rpm = MinRPM;
|
||||
}
|
||||
else if ( rpm > maxRPM )
|
||||
{
|
||||
|
||||
if ( rpm > maxRPM * 1.2f )
|
||||
availableTorque = 0;
|
||||
|
||||
rpm = maxRPM;
|
||||
|
||||
if ( Gear != MaxGear || groundedCount < Wheels.Count )
|
||||
isRedlining = true;
|
||||
}
|
||||
|
||||
FlywheelRPM = Math.Clamp( rpm, 0, maxRPM );
|
||||
|
||||
if ( burnout > 0 )
|
||||
availableTorque += availableTorque * burnout * 0.1f;
|
||||
|
||||
var front = 0.5f + PowerDistribution * 0.5f;
|
||||
var rear = 1 - front;
|
||||
|
||||
availableFrontTorque = availableTorque * front;
|
||||
availableRearTorque = availableTorque * rear;
|
||||
|
||||
throttle = MathX.Approach( throttle, inputThrottle, dt * 4 );
|
||||
|
||||
|
||||
EngineAccelerate( FlywheelFriction + FlywheelTorque * throttle, dt );
|
||||
|
||||
Throttle = throttle;
|
||||
|
||||
IsRedlining = (isRedlining && inputThrottle > 0);
|
||||
}
|
||||
|
||||
public void StreamEngineUpdate()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
46
Code/Car/VeloXCar.Steering.cs
Normal file
46
Code/Car/VeloXCar.Steering.cs
Normal file
@ -0,0 +1,46 @@
|
||||
using Sandbox;
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace VeloX;
|
||||
|
||||
public partial class VeloXCar
|
||||
{
|
||||
public static float ExpDecay( float a, float b, float decay, float dt ) => b + (a - b) * MathF.Exp( -decay * dt );
|
||||
[Property, Feature( "Steer" )] public float SteerConeMaxSpeed { get; set; } = 1800;
|
||||
[Property, Feature( "Steer" )] public float SteerConeMaxAngle { get; set; } = 0.25f;
|
||||
[Property, Feature( "Steer" )] public float SteerConeChangeRate { get; set; } = 8;
|
||||
[Property, Feature( "Steer" )] public float CounterSteer { get; set; } = 0.1f;
|
||||
[Property, Feature( "Steer" )] public float MaxSteerAngle { get; set; } = 35f;
|
||||
|
||||
[Sync] public float Steering { get; private set; }
|
||||
private float jTurnMultiplier;
|
||||
|
||||
private float inputSteer;
|
||||
private void UpdateSteering( float dt )
|
||||
{
|
||||
var inputSteer = Input.AnalogMove.y;
|
||||
var absInputSteer = Math.Abs( inputSteer );
|
||||
|
||||
var sideSlip = Math.Clamp( avgSideSlip, -1, 1 );
|
||||
|
||||
var steerConeFactor = Math.Clamp( TotalSpeed / SteerConeMaxSpeed, 0, 1 );
|
||||
var steerCone = 1 - steerConeFactor * (1 - SteerConeMaxAngle);
|
||||
|
||||
steerCone = Math.Clamp( steerCone, Math.Abs( sideSlip ), 1 );
|
||||
|
||||
inputSteer = ExpDecay( this.inputSteer, inputSteer * steerCone, SteerConeChangeRate, dt );
|
||||
this.inputSteer = inputSteer;
|
||||
var counterSteer = sideSlip * steerConeFactor * (1 - absInputSteer);
|
||||
counterSteer = Math.Clamp( counterSteer, -1, 1 ) * CounterSteer;
|
||||
|
||||
inputSteer = Math.Clamp( inputSteer + counterSteer, -1, 1 );
|
||||
Steering = inputSteer;
|
||||
SteerAngle = new( 0, inputSteer * MaxSteerAngle, 0 );
|
||||
|
||||
if ( ForwardSpeed < -100 )
|
||||
jTurnMultiplier = 0.5f;
|
||||
else
|
||||
jTurnMultiplier = ExpDecay( jTurnMultiplier, 1, 2, dt );
|
||||
}
|
||||
}
|
||||
46
Code/Car/VeloXCar.Wheel.cs
Normal file
46
Code/Car/VeloXCar.Wheel.cs
Normal file
@ -0,0 +1,46 @@
|
||||
namespace VeloX;
|
||||
|
||||
public partial class VeloXCar
|
||||
{
|
||||
|
||||
private void WheelThink( in float dt )
|
||||
{
|
||||
var maxRPM = GetTransmissionMaxRPM( Gear );
|
||||
|
||||
|
||||
var frontTorque = availableFrontTorque;
|
||||
var rearTorque = availableRearTorque;
|
||||
|
||||
|
||||
groundedCount = 0;
|
||||
float avgRPM = 0, totalSideSlip = 0, totalForwardSlip = 0;
|
||||
|
||||
foreach ( var w in Wheels )
|
||||
{
|
||||
w.Update( this, dt );
|
||||
|
||||
totalSideSlip += w.SideSlip;
|
||||
totalForwardSlip += w.ForwardSlip;
|
||||
var rpm = w.RPM;
|
||||
|
||||
avgRPM += rpm * w.DistributionFactor;
|
||||
|
||||
w.Torque = w.DistributionFactor * (w.IsFront ? frontTorque : rearTorque);
|
||||
w.Brake = w.IsFront ? frontBrake : rearBrake;
|
||||
|
||||
if ( inputHandbrake && !w.IsFront )
|
||||
w.RPM = 0;
|
||||
|
||||
if ( rpm > maxRPM )
|
||||
w.RPM = maxRPM;
|
||||
|
||||
if ( w.IsOnGround )
|
||||
groundedCount++;
|
||||
|
||||
}
|
||||
avgPoweredRPM = avgRPM;
|
||||
avgSideSlip = totalSideSlip / Wheels.Count;
|
||||
avgForwardSlip = totalForwardSlip / Wheels.Count;
|
||||
|
||||
}
|
||||
}
|
||||
60
Code/Car/VeloXCar.cs
Normal file
60
Code/Car/VeloXCar.cs
Normal file
@ -0,0 +1,60 @@
|
||||
using Sandbox;
|
||||
using System;
|
||||
|
||||
namespace VeloX;
|
||||
|
||||
[Group( "VeloX" )]
|
||||
[Title( "VeloX - Car" )]
|
||||
public partial class VeloXCar : VeloXBase
|
||||
{
|
||||
protected override void OnAwake()
|
||||
{
|
||||
base.OnAwake();
|
||||
|
||||
StreamPlayer = new( Stream );
|
||||
}
|
||||
protected override void OnStart()
|
||||
{
|
||||
base.OnStart();
|
||||
|
||||
if ( !IsProxy )
|
||||
{
|
||||
UpdateGearList();
|
||||
UpdatePowerDistribution();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnUpdate()
|
||||
{
|
||||
base.OnUpdate();
|
||||
if ( StreamPlayer is not null )
|
||||
{
|
||||
|
||||
StreamPlayer.Throttle = Throttle;
|
||||
StreamPlayer.RPMPercent = RPMPercent;
|
||||
StreamPlayer.EngineState = EngineState;
|
||||
StreamPlayer.IsRedlining = IsRedlining;
|
||||
|
||||
if ( Driver.IsValid() && Driver.IsProxy )
|
||||
StreamPlayer.Update( Time.Delta, Scene.Camera.WorldPosition );
|
||||
else
|
||||
StreamPlayer.Update( Time.Delta, WorldPosition );
|
||||
}
|
||||
|
||||
}
|
||||
protected override void OnFixedUpdate()
|
||||
{
|
||||
if ( IsProxy )
|
||||
return;
|
||||
|
||||
base.OnFixedUpdate();
|
||||
|
||||
var dt = Time.Delta;
|
||||
EngineThink( dt );
|
||||
WheelThink( dt );
|
||||
UpdateSteering( dt );
|
||||
|
||||
Brake = Math.Clamp( frontBrake + rearBrake + (Input.Down( "Jump" ) ? 1 : 0), 0, 1 );
|
||||
}
|
||||
|
||||
}
|
||||
9
Code/Enum/EngineState.cs
Normal file
9
Code/Enum/EngineState.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace VeloX;
|
||||
|
||||
public enum EngineState
|
||||
{
|
||||
Off,
|
||||
Starting,
|
||||
Running,
|
||||
ShuttingDown,
|
||||
}
|
||||
9
Code/Enum/WaterState.cs
Normal file
9
Code/Enum/WaterState.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace VeloX;
|
||||
|
||||
public enum WaterState
|
||||
{
|
||||
NotOnWater, // 0: Не на воде
|
||||
PartialContact, // 1: Хотя бы одна точка плавучести на воде
|
||||
HalfSubmerged, // 2: Хотя бы половина точек плавучести на воде
|
||||
FullySubmerged // 3: Полностью погружён
|
||||
}
|
||||
47
Code/Input/InputResolver.cs
Normal file
47
Code/Input/InputResolver.cs
Normal file
@ -0,0 +1,47 @@
|
||||
using Sandbox;
|
||||
|
||||
namespace VeloX;
|
||||
public class InputResolver
|
||||
{
|
||||
public GameObject Driver { get; internal set; }
|
||||
|
||||
|
||||
private bool IsDriverActive => Driver.IsValid();
|
||||
|
||||
|
||||
public Vector2 MouseDelta => IsDriverActive ? Input.MouseDelta : default;
|
||||
public Vector2 MouseWheel => IsDriverActive ? Input.MouseWheel : default;
|
||||
public Angles AnalogLook => IsDriverActive ? Input.AnalogLook : default;
|
||||
public Vector3 AnalogMove => IsDriverActive ? Input.AnalogMove : default;
|
||||
|
||||
public bool Down( string action )
|
||||
{
|
||||
return IsDriverActive && Input.Down( action );
|
||||
}
|
||||
|
||||
public bool Pressed( string action )
|
||||
{
|
||||
return IsDriverActive && Input.Pressed( action );
|
||||
}
|
||||
|
||||
public bool Released( string action )
|
||||
{
|
||||
return IsDriverActive && Input.Released( action );
|
||||
}
|
||||
|
||||
public void TriggerHaptics( float leftMotor, float rightMotor, float leftTrigger = 0f, float rightTrigger = 0f, int duration = 500 )
|
||||
{
|
||||
if ( IsDriverActive )
|
||||
{
|
||||
Input.TriggerHaptics( leftMotor, rightMotor, leftTrigger, rightTrigger, duration );
|
||||
}
|
||||
}
|
||||
|
||||
public void StopAllHaptics()
|
||||
{
|
||||
if ( IsDriverActive )
|
||||
{
|
||||
Input.StopAllHaptics();
|
||||
}
|
||||
}
|
||||
}
|
||||
87
Code/Utils/EngineStream/ControllerConverter.cs
Normal file
87
Code/Utils/EngineStream/ControllerConverter.cs
Normal file
@ -0,0 +1,87 @@
|
||||
using Sandbox;
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace VeloX.Audio;
|
||||
|
||||
[JsonConverter( typeof( ControllerConverter ) )]
|
||||
public sealed class Controller
|
||||
{
|
||||
public enum InputTypes
|
||||
{
|
||||
Throttle,
|
||||
RpmFraction,
|
||||
}
|
||||
public enum OutputTypes
|
||||
{
|
||||
Volume,
|
||||
Pitch,
|
||||
}
|
||||
|
||||
public required InputTypes InputParameter { get; set; }
|
||||
[Property] public ValueRange InputRange { get; set; }
|
||||
public required OutputTypes OutputParameter { get; set; }
|
||||
public ValueRange OutputRange { get; set; }
|
||||
}
|
||||
|
||||
public record struct ValueRange
|
||||
{
|
||||
public ValueRange( float inputMin, float inputMax )
|
||||
{
|
||||
Min = inputMin;
|
||||
Max = inputMax;
|
||||
}
|
||||
|
||||
[Range( 0, 1 )] public float Min { get; set; }
|
||||
[Range( 0, 1 )] public float Max { get; set; }
|
||||
|
||||
public readonly float Remap( float value, float newMin, float newMax )
|
||||
{
|
||||
var normalized = (value - Min) / (Max - Min);
|
||||
return newMin + normalized * (newMax - newMin);
|
||||
}
|
||||
};
|
||||
|
||||
public sealed class ControllerConverter : JsonConverter<Controller>
|
||||
{
|
||||
public override Controller Read( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options )
|
||||
{
|
||||
if ( reader.TokenType != JsonTokenType.StartArray )
|
||||
throw new JsonException( "Expected array for Controller" );
|
||||
|
||||
reader.Read();
|
||||
string inputParam = reader.GetString() ?? string.Empty;
|
||||
reader.Read();
|
||||
float inputMin = reader.GetSingle();
|
||||
reader.Read();
|
||||
float inputMax = reader.GetSingle();
|
||||
reader.Read();
|
||||
string outputParam = reader.GetString() ?? string.Empty;
|
||||
reader.Read();
|
||||
float outputMin = reader.GetSingle();
|
||||
reader.Read();
|
||||
float outputMax = reader.GetSingle();
|
||||
reader.Read(); // End of array
|
||||
|
||||
return new Controller
|
||||
{
|
||||
InputParameter = Enum.Parse<Controller.InputTypes>( inputParam, true ),
|
||||
InputRange = new ValueRange( inputMin, inputMax ),
|
||||
OutputParameter = Enum.Parse<Controller.OutputTypes>( outputParam, true ),
|
||||
OutputRange = new ValueRange( outputMin, outputMax )
|
||||
};
|
||||
}
|
||||
|
||||
public override void Write( Utf8JsonWriter writer, Controller value, JsonSerializerOptions options )
|
||||
{
|
||||
writer.WriteStartArray();
|
||||
writer.WriteStringValue( value.InputParameter.ToString() );
|
||||
writer.WriteNumberValue( value.InputRange.Min );
|
||||
writer.WriteNumberValue( value.InputRange.Max );
|
||||
writer.WriteStringValue( value.OutputParameter.ToString() );
|
||||
writer.WriteNumberValue( value.OutputRange.Min );
|
||||
writer.WriteNumberValue( value.OutputRange.Max );
|
||||
writer.WriteEndArray();
|
||||
}
|
||||
}
|
||||
35
Code/Utils/EngineStream/EngineStream.cs
Normal file
35
Code/Utils/EngineStream/EngineStream.cs
Normal file
@ -0,0 +1,35 @@
|
||||
using Sandbox;
|
||||
using System.Collections.Generic;
|
||||
using VeloX.Audio;
|
||||
|
||||
namespace VeloX;
|
||||
|
||||
[GameResource( "Engine Stream", "engstr", "Engine Sound", Category = "VeloX", Icon = "time_to_leave" )]
|
||||
public sealed class EngineStream : GameResource
|
||||
{
|
||||
public sealed class Layer
|
||||
{
|
||||
[Property,
|
||||
Title( "Sound File" ),
|
||||
Description( "Sound asset for this layer" )]
|
||||
public SoundFile AudioPath { get; set; }
|
||||
|
||||
[Property, Title( "Use Redline Effect" ),
|
||||
Description( "Enable RPM-based vibration effect" )]
|
||||
public bool UseRedline { get; set; } = true;
|
||||
|
||||
[Property, Title( "Controllers" ),
|
||||
Description( "Audio parameter controllers" )]
|
||||
public List<Controller> Controllers { get; set; }
|
||||
[Property, Title( "Is Muted" )]
|
||||
public bool IsMuted { get; set; }
|
||||
}
|
||||
|
||||
[Property, Title( "Stream Parameters" ),
|
||||
Description( "Global engine sound parameters" )]
|
||||
public StreamParameters Parameters { get; set; }
|
||||
|
||||
[Property, Title( "Sound Layers" ),
|
||||
Description( "Individual sound layers" )]
|
||||
public Dictionary<string, Layer> Layers { get; set; }
|
||||
}
|
||||
119
Code/Utils/EngineStream/EngineStreamPlayer.cs
Normal file
119
Code/Utils/EngineStream/EngineStreamPlayer.cs
Normal file
@ -0,0 +1,119 @@
|
||||
using Sandbox;
|
||||
using Sandbox.Audio;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using VeloX.Audio;
|
||||
using static VeloX.EngineStream;
|
||||
|
||||
namespace VeloX;
|
||||
|
||||
public class EngineStreamPlayer( EngineStream stream ) : IDisposable
|
||||
{
|
||||
|
||||
private static readonly Mixer EngineMixer = Mixer.FindMixerByName( "Car Engine" );
|
||||
public EngineStream Stream { get; set; } = stream;
|
||||
|
||||
public EngineState EngineState { get; set; }
|
||||
public bool EngineSoundPaused => EngineState != EngineState.Running;
|
||||
|
||||
public float Throttle { get; set; }
|
||||
public bool IsRedlining { get; set; }
|
||||
public float RPMPercent { get; set; }
|
||||
|
||||
private float _wobbleTime;
|
||||
|
||||
public readonly Dictionary<Layer, SoundHandle> EngineSounds = [];
|
||||
|
||||
public void Update( float deltaTime, Vector3 position, bool isLocal = false )
|
||||
{
|
||||
var globalPitch = 1.0f;
|
||||
|
||||
// Gear wobble effect
|
||||
if ( _wobbleTime > 0 )
|
||||
{
|
||||
_wobbleTime -= deltaTime * (0.1f + Throttle);
|
||||
globalPitch += MathF.Cos( _wobbleTime * Stream.Parameters.WobbleFrequency ) * _wobbleTime * (1 - _wobbleTime) * Stream.Parameters.WobbleStrength;
|
||||
}
|
||||
|
||||
globalPitch *= Stream.Parameters.Pitch;
|
||||
|
||||
// Redline effect
|
||||
var redlineVolume = 1.0f;
|
||||
if ( IsRedlining )
|
||||
{
|
||||
redlineVolume = 1 - Stream.Parameters.RedlineStrength +
|
||||
MathF.Cos( RealTime.Now * Stream.Parameters.RedlineFrequency ) *
|
||||
Stream.Parameters.RedlineStrength;
|
||||
|
||||
}
|
||||
// Process layers
|
||||
foreach ( var (id, layer) in Stream.Layers )
|
||||
{
|
||||
EngineSounds.TryGetValue( layer, out var channel );
|
||||
|
||||
if ( !channel.IsValid() )
|
||||
{
|
||||
channel = Sound.PlayFile( layer.AudioPath );
|
||||
EngineSounds[layer] = channel;
|
||||
}
|
||||
|
||||
if ( channel.Paused && (EngineSoundPaused || layer.IsMuted) )
|
||||
continue;
|
||||
|
||||
// Reset controller outputs
|
||||
float layerVolume = 1.0f;
|
||||
float layerPitch = 1.0f;
|
||||
|
||||
// Apply all controllers
|
||||
foreach ( var controller in layer.Controllers )
|
||||
{
|
||||
var inputValue = controller.InputParameter switch
|
||||
{
|
||||
Controller.InputTypes.Throttle => Throttle,
|
||||
Controller.InputTypes.RpmFraction => RPMPercent,
|
||||
_ => 0.0f
|
||||
};
|
||||
|
||||
var normalized = Math.Clamp( inputValue, controller.InputRange.Min, controller.InputRange.Max );
|
||||
var outputValue = controller.InputRange.Remap(
|
||||
normalized,
|
||||
controller.OutputRange.Min,
|
||||
controller.OutputRange.Max
|
||||
);
|
||||
|
||||
// Apply to correct parameter
|
||||
switch ( controller.OutputParameter )
|
||||
{
|
||||
case Controller.OutputTypes.Volume:
|
||||
layerVolume *= outputValue;
|
||||
break;
|
||||
case Controller.OutputTypes.Pitch:
|
||||
layerPitch *= outputValue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply redline effect if needed
|
||||
layerVolume *= layer.UseRedline ? redlineVolume : 1.0f;
|
||||
layerPitch *= globalPitch;
|
||||
// Update audio channel
|
||||
channel.Pitch = layerPitch;
|
||||
channel.Volume = layerVolume * Stream.Parameters.Volume;
|
||||
channel.Position = position;
|
||||
channel.ListenLocal = isLocal;
|
||||
channel.Paused = EngineSoundPaused || layer.IsMuted;
|
||||
channel.TargetMixer = EngineMixer;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach ( var item in EngineSounds )
|
||||
{
|
||||
item.Value?.Stop();
|
||||
item.Value?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
24
Code/Utils/EngineStream/StreamParameters.cs
Normal file
24
Code/Utils/EngineStream/StreamParameters.cs
Normal file
@ -0,0 +1,24 @@
|
||||
using Sandbox;
|
||||
|
||||
namespace VeloX;
|
||||
|
||||
public sealed class StreamParameters
|
||||
{
|
||||
[Property, Range( 0.1f, 2.0f ), Description( "Base pitch multiplier" )]
|
||||
public float Pitch { get; set; } = 1.0f;
|
||||
|
||||
[Property, Range( 0.0f, 2.0f ), Description( "Base volume level" )]
|
||||
public float Volume { get; set; } = 1.0f;
|
||||
|
||||
[Property, Range( 1f, 100f ), Description( "Vibration frequency at max RPM" )]
|
||||
public float RedlineFrequency { get; set; } = 55.0f;
|
||||
|
||||
[Property, Range( 0.0f, 1.0f ), Description( "Vibration intensity at max RPM" )]
|
||||
public float RedlineStrength { get; set; } = 0.2f;
|
||||
|
||||
[Property, Range( 1f, 50f ), Description( "Idle vibration frequency" )]
|
||||
public float WobbleFrequency { get; set; } = 25.0f;
|
||||
|
||||
[Property, Range( 0.0f, 0.5f ), Description( "Idle vibration intensity" )]
|
||||
public float WobbleStrength { get; set; } = 0.13f;
|
||||
}
|
||||
78
Code/Utils/PhysicsExtensions.cs
Normal file
78
Code/Utils/PhysicsExtensions.cs
Normal file
@ -0,0 +1,78 @@
|
||||
|
||||
using Sandbox;
|
||||
|
||||
namespace VeloX;
|
||||
public static class PhysicsExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Calculates the linear and angular velocities on the center of mass for an offset impulse.
|
||||
/// </summary>
|
||||
/// <param name="physObj">The physics object</param>
|
||||
/// <param name="impulse">The impulse acting on the object in kg*units/s (World frame)</param>
|
||||
/// <param name="position">The location of the impulse in world coordinates</param>
|
||||
/// <returns>
|
||||
/// Vector1: Linear velocity from the impulse (World frame)
|
||||
/// Vector2: Angular velocity from the impulse (Local frame)
|
||||
/// </returns>
|
||||
public static (Vector3 LinearVelocity, Vector3 AngularVelocity) CalculateVelocityOffset( this PhysicsBody physObj, Vector3 impulse, Vector3 position )
|
||||
{
|
||||
if ( !physObj.IsValid() || !physObj.MotionEnabled )
|
||||
return (Vector3.Zero, Vector3.Zero);
|
||||
|
||||
|
||||
Vector3 linearVelocity = impulse / physObj.Mass;
|
||||
|
||||
Vector3 centerOfMass = physObj.MassCenter;
|
||||
Vector3 relativePosition = position - centerOfMass;
|
||||
Vector3 torque = relativePosition.Cross( impulse );
|
||||
|
||||
Rotation bodyRotation = physObj.Transform.Rotation;
|
||||
Vector3 localTorque = bodyRotation.Inverse * torque;
|
||||
|
||||
Vector3 localInverseInertia = physObj.Inertia.Inverse;
|
||||
|
||||
Vector3 localAngularVelocity = new(
|
||||
localTorque.x * localInverseInertia.x,
|
||||
localTorque.y * localInverseInertia.y,
|
||||
localTorque.z * localInverseInertia.z );
|
||||
|
||||
return (linearVelocity, localAngularVelocity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the linear and angular impulses on the object's center of mass for an offset impulse.
|
||||
/// </summary>
|
||||
/// <param name="physObj">The physics object</param>
|
||||
/// <param name="impulse">The impulse acting on the object in kg*units/s (World frame)</param>
|
||||
/// <param name="position">The location of the impulse in world coordinates</param>
|
||||
/// <returns>
|
||||
/// Vector1: Linear impulse on center of mass (World frame)
|
||||
/// Vector2: Angular impulse on center of mass (Local frame)
|
||||
/// </returns>
|
||||
public static (Vector3 LinearImpulse, Vector3 AngularImpulse) CalculateForceOffset(
|
||||
this PhysicsBody physObj,
|
||||
Vector3 impulse,
|
||||
Vector3 position )
|
||||
{
|
||||
if ( !physObj.IsValid() || !physObj.MotionEnabled )
|
||||
{
|
||||
return (Vector3.Zero, Vector3.Zero);
|
||||
}
|
||||
|
||||
// 1. Linear impulse is the same as the input impulse (conservation of momentum)
|
||||
Vector3 linearImpulse = impulse;
|
||||
|
||||
// 2. Calculate angular impulse (torque) from the offset force
|
||||
// τ = r × F (cross product of position relative to COM and force)
|
||||
Vector3 centerOfMass = physObj.MassCenter;
|
||||
Vector3 relativePosition = position - centerOfMass;
|
||||
Vector3 worldAngularImpulse = relativePosition.Cross( impulse );
|
||||
|
||||
// Convert angular impulse to local space (since we'll use it with LocalInertia)
|
||||
Rotation bodyRotation = physObj.Transform.Rotation;
|
||||
Vector3 localAngularImpulse = bodyRotation.Inverse * worldAngularImpulse;
|
||||
|
||||
return (linearImpulse, localAngularImpulse);
|
||||
}
|
||||
|
||||
}
|
||||
4
Code/velox.csproj.user
Normal file
4
Code/velox.csproj.user
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup />
|
||||
</Project>
|
||||
30
Editor/ControllerWidget.cs
Normal file
30
Editor/ControllerWidget.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using Editor;
|
||||
using Sandbox;
|
||||
using VeloX.Audio;
|
||||
|
||||
namespace VeloX;
|
||||
|
||||
[CustomEditor( typeof( Controller ) )]
|
||||
public class ControllerWidget : ControlObjectWidget
|
||||
{
|
||||
public override bool SupportsMultiEdit => false;
|
||||
public override bool IsWideMode => true;
|
||||
|
||||
public ControllerWidget( SerializedProperty property ) : base( property, true )
|
||||
{
|
||||
|
||||
Layout = Layout.Column();
|
||||
|
||||
SerializedObject.TryGetProperty( nameof( Controller.InputParameter ), out var inputType );
|
||||
Layout.Add( Create( inputType ) );
|
||||
SerializedObject.TryGetProperty( nameof( Controller.InputRange ), out var inputRange );
|
||||
Layout.Add( Create( inputRange ) );
|
||||
|
||||
SerializedObject.TryGetProperty( nameof( Controller.OutputParameter ), out var outputType );
|
||||
Layout.Add( Create( outputType ) );
|
||||
SerializedObject.TryGetProperty( nameof( Controller.OutputRange ), out var outputRange );
|
||||
Layout.Add( Create( outputRange ) );
|
||||
}
|
||||
|
||||
protected override void OnPaint() { }
|
||||
}
|
||||
144
Editor/ESEditor/EngineStreamEditor.cs
Normal file
144
Editor/ESEditor/EngineStreamEditor.cs
Normal file
@ -0,0 +1,144 @@
|
||||
using Editor;
|
||||
using Sandbox;
|
||||
using static Editor.Inspectors.AssetInspector;
|
||||
using static VeloX.EngineStream;
|
||||
|
||||
namespace VeloX;
|
||||
[Dock( "Editor", "Engine Stream Editor", "local_fire_department" )]
|
||||
[CanEdit( "asset:engstr" )]
|
||||
internal sealed class EngineStreamEditor : Widget, IAssetInspector
|
||||
{
|
||||
|
||||
public EngineStreamEditor( Widget parent ) : base( parent, false )
|
||||
{
|
||||
CreateEditor();
|
||||
}
|
||||
public static EngineStreamEditor Instance { get; private set; }
|
||||
private EngineStream ActiveStream;
|
||||
private SerializedObject StreamSO;
|
||||
private ScrollArea StreamTabs;
|
||||
|
||||
//[EditorEvent.Hotload]
|
||||
//internal void Reload()
|
||||
//{
|
||||
// if ( Instance.IsValid() )
|
||||
// CreateEditor();
|
||||
//}
|
||||
|
||||
//[Menu( "Editor", "VeloX/Engine Stream Editor", "time_to_leave" )]
|
||||
internal static void Open()
|
||||
{
|
||||
Instance = new EngineStreamEditor();
|
||||
}
|
||||
|
||||
internal EngineStreamEditor()
|
||||
{
|
||||
|
||||
Size = new Vector2( 1300, 700 );
|
||||
CreateEditor();
|
||||
Show();
|
||||
}
|
||||
|
||||
private class LayerObject
|
||||
{
|
||||
public Layer Layer { get; set; }
|
||||
public string Id { get; set; }
|
||||
}
|
||||
|
||||
|
||||
private void CreateEditor()
|
||||
{
|
||||
WindowTitle = "Engine Stream Editor";
|
||||
Layout = Layout.Column();
|
||||
//Canvas = new( null )
|
||||
//{
|
||||
// Layout = Layout,
|
||||
//};
|
||||
|
||||
CreateHeader();
|
||||
|
||||
var b = Layout.AddRow();
|
||||
|
||||
CreateTabs( b );
|
||||
CreateRightMenu( b );
|
||||
}
|
||||
private void CreateHeader()
|
||||
{
|
||||
|
||||
var a = Layout.AddRow();
|
||||
a.Margin = new Sandbox.UI.Margin( 8, 12, 8, 16 );
|
||||
a.Alignment = TextFlag.Left;
|
||||
a.Spacing = 12;
|
||||
|
||||
var fileButton = a.Add( new Button( "Open File" ), 0 );
|
||||
fileButton.Clicked = () =>
|
||||
{
|
||||
var picker = AssetPicker.Create( this,
|
||||
AssetType.FromType( typeof( EngineStream ) ),
|
||||
new() { EnableCloud = true, SeparateCloudTab = false }
|
||||
);
|
||||
|
||||
picker.OnAssetPicked += asset => CreateStreamEditor( asset[0].LoadResource<EngineStream>() );
|
||||
picker.Show();
|
||||
};
|
||||
}
|
||||
Layout RightLayout;
|
||||
SimulatedEngineWidget SimulatedEngineWidget;
|
||||
private void CreateRightMenu( Layout layout )
|
||||
{
|
||||
RightLayout = layout.AddColumn( 1 );
|
||||
RightLayout.Alignment = TextFlag.Top;
|
||||
RebuildRightMenu();
|
||||
}
|
||||
|
||||
private void RebuildRightMenu()
|
||||
{
|
||||
|
||||
RightLayout.Clear( true );
|
||||
RightLayout.Spacing = 12;
|
||||
var layoutTop = RightLayout.AddColumn();
|
||||
SimulatedEngineWidget = new SimulatedEngineWidget( null, ActiveStream );
|
||||
layoutTop.Add( SimulatedEngineWidget );
|
||||
if ( StreamSO is not null )
|
||||
layoutTop.Add( new ParametersWidget( StreamSO.GetProperty( nameof( EngineStream.Parameters ) ) ) );
|
||||
|
||||
}
|
||||
|
||||
private void CreateTabs( Layout layout )
|
||||
{
|
||||
|
||||
StreamTabs = layout.Add( new ScrollArea( this ), 5 );
|
||||
StreamTabs.Canvas = new Widget( StreamTabs )
|
||||
{
|
||||
Layout = Layout.Column(),
|
||||
VerticalSizeMode = (SizeMode)3,
|
||||
};
|
||||
|
||||
StreamTabs.Canvas.Layout.Margin = new Sandbox.UI.Margin( 8, 8, 16, 8 );
|
||||
StreamTabs.Canvas.Layout.Alignment = TextFlag.Top;
|
||||
|
||||
}
|
||||
|
||||
public void RebuildLayers()
|
||||
{
|
||||
StreamTabs.Canvas.Layout.Clear( true );
|
||||
StreamSO = ActiveStream.GetSerialized();
|
||||
|
||||
if ( !StreamSO.TryGetProperty( nameof( EngineStream.Layers ), out var layers ) )
|
||||
return;
|
||||
|
||||
StreamTabs.Canvas.Layout.Add( new LayersEditorWidget( layers, ActiveStream, SimulatedEngineWidget ) );
|
||||
}
|
||||
|
||||
private void CreateStreamEditor( EngineStream stream )
|
||||
{
|
||||
ActiveStream = stream;
|
||||
StreamSO = ActiveStream.GetSerialized();
|
||||
RebuildRightMenu();
|
||||
RebuildLayers();
|
||||
}
|
||||
public void SetAsset( Asset asset )
|
||||
{
|
||||
CreateStreamEditor( asset.LoadResource<EngineStream>() );
|
||||
}
|
||||
}
|
||||
154
Editor/ESEditor/LayerObjectWidget.cs
Normal file
154
Editor/ESEditor/LayerObjectWidget.cs
Normal file
@ -0,0 +1,154 @@
|
||||
|
||||
using Editor;
|
||||
using Sandbox;
|
||||
using System.Numerics;
|
||||
using VeloX.Audio;
|
||||
using static VeloX.EngineStream;
|
||||
|
||||
namespace VeloX;
|
||||
internal class LayerObjectWidget : ControlObjectWidget
|
||||
{
|
||||
private SerializedProperty Index;
|
||||
|
||||
private SerializedCollection Controllers;
|
||||
private class ControllerWidget : ControlObjectWidget
|
||||
{
|
||||
public ControllerWidget( LayerObjectWidget parent, SerializedProperty property ) : base( property, true )
|
||||
{
|
||||
PaintBackground = false;
|
||||
Layout = Layout.Row();
|
||||
HorizontalSizeMode = (SizeMode)3;
|
||||
//SetStyles( "background-color: #123; color: white; font-weight: 600;" );
|
||||
SerializedObject.TryGetProperty( nameof( Controller.InputParameter ), out var inputType );
|
||||
Layout.Add( EnumControlWidget.Create( inputType ), 1 );
|
||||
SerializedObject.TryGetProperty( nameof( Controller.InputRange ), out var inputRange );
|
||||
Layout.Add( ValueRangeWidget.Create( inputRange ), 12 );
|
||||
|
||||
SerializedObject.TryGetProperty( nameof( Controller.OutputParameter ), out var outputType );
|
||||
Layout.Add( EnumControlWidget.Create( outputType ), 1 );
|
||||
SerializedObject.TryGetProperty( nameof( Controller.OutputRange ), out var outputRange );
|
||||
Layout.Add( ValueRangeWidget.Create( outputRange ), 12 );
|
||||
var removeButton = new IconButton( "clear", () => parent.RemoveEntry( property ) ) { ToolTip = "Remove", Background = Theme.ControlBackground, FixedWidth = Theme.RowHeight, FixedHeight = Theme.RowHeight };
|
||||
Layout.Add( removeButton );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void RemoveEntry( SerializedProperty index )
|
||||
{
|
||||
Controllers.Remove( index );
|
||||
Rebuild();
|
||||
}
|
||||
private SerializedCollection layers;
|
||||
|
||||
SimulatedEngineWidget SimulatedEngine;
|
||||
public LayerObjectWidget( SerializedProperty property, SerializedProperty index, SerializedCollection layers, SimulatedEngineWidget SimulatedEngine ) : base( property, true )
|
||||
{
|
||||
|
||||
this.SimulatedEngine = SimulatedEngine;
|
||||
SetStyles( "background-color: #;" );
|
||||
|
||||
this.layers = layers;
|
||||
Index = index;
|
||||
Layout = Layout.Column();
|
||||
Layout.SizeConstraint = SizeConstraint.SetMaximumSize;
|
||||
Layout.Alignment = TextFlag.Top;
|
||||
Layout.Margin = 4;
|
||||
Layout.Spacing = 4;
|
||||
|
||||
Rebuild();
|
||||
}
|
||||
protected override void PaintUnder()
|
||||
{
|
||||
|
||||
base.PaintUnder();
|
||||
|
||||
if ( SimulatedEngine is null )
|
||||
return;
|
||||
if ( SimulatedEngine.Player is null )
|
||||
return;
|
||||
|
||||
var layer = SimulatedEngine.Player.Stream.Layers[Index.GetValue<string>()];
|
||||
if ( SimulatedEngine.Player.EngineSounds.TryGetValue( layer, out var handle ) )
|
||||
{
|
||||
|
||||
Paint.SetBrush( Color.Gray );
|
||||
Rect localRect = base.LocalRect;
|
||||
var newVolume = localRect.Width * handle.Volume;
|
||||
|
||||
Paint.DrawRect( new( 0, 0, newVolume, localRect.Height ) );
|
||||
|
||||
Update();
|
||||
}
|
||||
}
|
||||
|
||||
public void Rebuild()
|
||||
{
|
||||
using var _ = SuspendUpdates.For( this );
|
||||
Layout.Clear( true );
|
||||
VerticalSizeMode = SizeMode.CanShrink;
|
||||
|
||||
var layout = Layout.AddRow();
|
||||
layout.Alignment = TextFlag.Top;
|
||||
var id = layout.Add(
|
||||
StringControlWidget.Create( Index ), 1
|
||||
).HorizontalSizeMode = SizeMode.Flexible;
|
||||
|
||||
layout.Add(
|
||||
ResourceControlWidget.Create( SerializedObject.GetProperty( nameof( Layer.AudioPath ) ) ), 3
|
||||
).HorizontalSizeMode = SizeMode.Flexible;
|
||||
|
||||
var ctlrs = Layout.AddColumn();
|
||||
ctlrs.Spacing = 2;
|
||||
ctlrs.Margin = 4;
|
||||
|
||||
var hints = ctlrs.AddRow();
|
||||
hints.Add( new Label( "Input Type" ), 3 );
|
||||
hints.Add( new Label( "Input Min" ), 2 );
|
||||
hints.Add( new Label( "Input Max" ), 2 );
|
||||
hints.Add( new Label( "Output Type" ), 3 );
|
||||
hints.Add( new Label( "Output Min" ), 2 );
|
||||
hints.Add( new Label( "Output Max" ), 2 );
|
||||
|
||||
if ( !SerializedObject.TryGetProperty( nameof( Layer.Controllers ), out var constrollers ) )
|
||||
return;
|
||||
|
||||
if ( !constrollers.TryGetAsObject( out var so ) || so is not SerializedCollection sc )
|
||||
return;
|
||||
|
||||
Controllers = sc;
|
||||
Controllers.OnEntryAdded = Rebuild;
|
||||
Controllers.OnEntryRemoved = Rebuild;
|
||||
foreach ( var controller in sc )
|
||||
{
|
||||
ctlrs.Add( new ControllerWidget( this, controller ) );
|
||||
}
|
||||
|
||||
var footer = ctlrs.AddRow();
|
||||
var controls = footer.AddRow();
|
||||
controls.Alignment = TextFlag.Left;
|
||||
controls.Spacing = 4;
|
||||
|
||||
var useRedline = SerializedObject.GetProperty( nameof( Layer.UseRedline ) );
|
||||
controls.Add( new BoolControlWidget( useRedline ) );
|
||||
controls.Add( new Label( useRedline.Description ) );
|
||||
|
||||
var isMuted = SerializedObject.GetProperty( nameof( Layer.IsMuted ) );
|
||||
controls.Add( new BoolControlWidget( isMuted ) );
|
||||
controls.Add( new Label( isMuted.DisplayName ) );
|
||||
|
||||
var cont = footer.AddRow();
|
||||
cont.Spacing = 4;
|
||||
cont.Alignment = TextFlag.Right;
|
||||
cont.Add( new Button( "Add Controller" ) ).Clicked = () =>
|
||||
{
|
||||
Controllers.Add( null );
|
||||
};
|
||||
|
||||
cont.Add( new Button( "Remove Layer" ) ).Clicked = () =>
|
||||
{
|
||||
layers.RemoveAt( Index.GetValue<string>() );
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
49
Editor/ESEditor/LayersEditorWidget.cs
Normal file
49
Editor/ESEditor/LayersEditorWidget.cs
Normal file
@ -0,0 +1,49 @@
|
||||
|
||||
using Editor;
|
||||
using Sandbox;
|
||||
using System.IO;
|
||||
|
||||
namespace VeloX;
|
||||
|
||||
|
||||
internal sealed class LayersEditorWidget : ControlObjectWidget
|
||||
{
|
||||
public EngineStream Stream;
|
||||
SimulatedEngineWidget SimulatedEngineWidget;
|
||||
public LayersEditorWidget( SerializedProperty property, EngineStream stream, SimulatedEngineWidget SimulatedEngine ) : base( property, true )
|
||||
{
|
||||
SimulatedEngineWidget = SimulatedEngine;
|
||||
PaintBackground = false;
|
||||
Stream = stream;
|
||||
Layout = Layout.Column();
|
||||
Layout.SizeConstraint = SizeConstraint.SetMaximumSize;
|
||||
Layout.Spacing = 12;
|
||||
|
||||
SetStyles( "background-color:rgba(0,0,0,0);" );
|
||||
|
||||
if ( SerializedObject is not SerializedCollection sc )
|
||||
return;
|
||||
sc.OnEntryAdded += Rebuild;
|
||||
sc.OnEntryRemoved += Rebuild;
|
||||
|
||||
Rebuild();
|
||||
}
|
||||
|
||||
private void Rebuild()
|
||||
{
|
||||
Layout.Clear( true );
|
||||
if ( SerializedObject is not SerializedCollection sc )
|
||||
return;
|
||||
|
||||
foreach ( var t in sc )
|
||||
{
|
||||
var id = t.GetKey();
|
||||
Layout.Add( new LayerObjectWidget( t, id, sc, SimulatedEngineWidget ) );
|
||||
}
|
||||
Layout.Add( new Button( "Add Layer" ) ).Clicked = () =>
|
||||
{
|
||||
sc.Add( "", null );
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user