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