This commit is contained in:
orosmatthew 2021-12-29 15:08:26 -05:00
parent 430e05e921
commit 7e968df4a6
17 changed files with 188 additions and 83 deletions

11
Main.cs
View File

@ -12,14 +12,17 @@ public class Main : Node2D
public override void _Ready() public override void _Ready()
{ {
_particleNodes = GetNode<Node2D>("ParticlesNodes"); _particleNodes = GetNode<Node2D>("ParticleNodes");
GD.Randomize(); GD.Randomize();
var randomSeed = GD.Randi(); ulong randomSeed = GD.Randi();
GD.Seed(randomSeed); GD.Seed(randomSeed);
GD.Print("Last Seed: " + randomSeed); GD.Print("Last Seed: " + randomSeed);
var viewSize = GetViewportRect().Size;
var zoom = GetNode<Camera2D>("Camera2D").Zoom;
var spaceSize = new Vector2(viewSize.x * zoom.x, viewSize.y * zoom.y);
_particleSimulation = new ParticleSimulation _particleSimulation = new ParticleSimulation
{ {
SpaceSize = GetViewportRect().Size * 1.15f SpaceSize = spaceSize
}; };
_particleSimulation.Initialize(); _particleSimulation.Initialize();
foreach (var id in _particleSimulation.LastParticlesAdded) CreateParticleNode(id); foreach (var id in _particleSimulation.LastParticlesAdded) CreateParticleNode(id);
@ -51,7 +54,7 @@ public class Main : Node2D
particleNode.CurrentSimulationPosition = simulationParticle.Position; particleNode.CurrentSimulationPosition = simulationParticle.Position;
particleNode.SetColor(simulationParticle.Type.Hue, simulationParticle.Health, particleNode.SetColor(simulationParticle.Type.Hue, simulationParticle.Health,
Mathf.Clamp(simulationParticle.AverageSpeed / 1.5f, 1f, 1f)); Mathf.Clamp(simulationParticle.AverageSpeed / 1.5f, 1f, 1f));
particleNode.ScreenWrappedLast = simulationParticle.ScreenWrappedLast; particleNode.WasTeleportedLast = simulationParticle.WasTeleportedLast;
} }
} }

View File

@ -5,9 +5,9 @@
[node name="Main" type="Node2D"] [node name="Main" type="Node2D"]
script = ExtResource( 1 ) script = ExtResource( 1 )
[node name="ParticlesNodes" type="Node2D" parent="."] [node name="ParticleNodes" type="Node2D" parent="."]
[node name="Camera2D" type="Camera2D" parent="."] [node name="Camera2D" type="Camera2D" parent="."]
anchor_mode = 0 anchor_mode = 0
current = true current = true
zoom = Vector2( 1.15, 1.15 ) zoom = Vector2( 1.35, 1.35 )

View File

@ -9,7 +9,7 @@ public class ParticleNode : Node2D
public Vector2 CurrentSimulationPosition = new Vector2(); public Vector2 CurrentSimulationPosition = new Vector2();
public Vector2 LastSimulationPosition; public Vector2 LastSimulationPosition;
public bool ScreenWrappedLast = false; public bool WasTeleportedLast = true;
public int SimulationId; public int SimulationId;
public override void _Ready() public override void _Ready()
@ -20,7 +20,7 @@ public class ParticleNode : Node2D
public override void _Process(float delta) public override void _Process(float delta)
{ {
Position = ScreenWrappedLast == false Position = WasTeleportedLast == false
? LastSimulationPosition.LinearInterpolate(CurrentSimulationPosition, ? LastSimulationPosition.LinearInterpolate(CurrentSimulationPosition,
GetParent<Node2D>().GetParent<Main>().PhysicsInterpolationFraction) GetParent<Node2D>().GetParent<Main>().PhysicsInterpolationFraction)
: CurrentSimulationPosition; : CurrentSimulationPosition;

View File

@ -1,7 +1,7 @@
[gd_scene load_steps=3 format=2] [gd_scene load_steps=3 format=2]
[ext_resource path="res://ParticleNode.cs" type="Script" id=1] [ext_resource path="res://ParticleNode.cs" type="Script" id=1]
[ext_resource path="res://textures/particle.png" type="Texture" id=2] [ext_resource path="res://textures/particle_noborder.png" type="Texture" id=2]
[node name="ParticleNode" type="Node2D"] [node name="ParticleNode" type="Node2D"]
script = ExtResource( 1 ) script = ExtResource( 1 )

View File

@ -7,7 +7,7 @@ namespace Particles.ParticleSimulation
private float _health = 1f; private float _health = 1f;
public Vector2 Position = new Vector2(); public Vector2 Position = new Vector2();
public bool ScreenWrappedLast = true; public bool WasTeleportedLast = true;
public Vector2 Velocity = new Vector2(); public Vector2 Velocity = new Vector2();
public Particle(int id, ParticleType type) public Particle(int id, ParticleType type)
@ -33,5 +33,10 @@ namespace Particles.ParticleSimulation
{ {
AverageSpeed = 0.99f * AverageSpeed + 0.01f * speed; AverageSpeed = 0.99f * AverageSpeed + 0.01f * speed;
} }
public void ResetAverageSpeed()
{
AverageSpeed = 1f;
}
} }
} }

View File

@ -1,27 +1,39 @@
using System.Collections.Generic; #define MULTITHREADED
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Linq;
using Godot; using Godot;
#if MULTITHREADED
using System.Threading.Tasks;
#endif
namespace Particles.ParticleSimulation namespace Particles.ParticleSimulation
{ {
public class ParticleSimulation public class ParticleSimulation
{ {
// size of simulation space
public Vector2 SpaceSize; public Vector2 SpaceSize;
// dictionary of particles with particle Id being the key
private readonly Dictionary<int, Particle> _particles = new Dictionary<int, Particle>(); private readonly Dictionary<int, Particle> _particles = new Dictionary<int, Particle>();
private readonly List<ParticleType> _particleTypes = new List<ParticleType>(); private readonly List<ParticleType> _particleTypes = new List<ParticleType>();
private readonly List<Task> _tasks = new List<Task>();
// task list if multi-threaded
#if MULTITHREADED
private readonly List<Task> _tasks = new List<Task>();
#endif
// updated on every simulation update
public List<int> LastParticlesAdded { get; private set; } = new List<int>(); public List<int> LastParticlesAdded { get; private set; } = new List<int>();
// ReSharper disable once CollectionNeverUpdated.Global
public List<int> LastParticlesRemoved { get; private set; } = new List<int>(); public List<int> LastParticlesRemoved { get; private set; } = new List<int>();
// counts up for each particle added
private int _idCount; private int _idCount;
private const int MaxParticles = 1100; private const int MaxParticles = 1100;
private const int MaxParticleTypes = 12; private const int MaxParticleTypes = 10;
private const float HealthDelta = 0.005f; private const float HealthDelta = 0.005f;
private const float NegativeHealthMultiplier = 2f; private const float NegativeHealthMultiplier = 2f;
private const float PositiveHealthMultiplier = 4f; private const float PositiveHealthMultiplier = 4f;
@ -32,35 +44,41 @@ namespace Particles.ParticleSimulation
{ {
for (var i = 0; i < MaxParticleTypes; i++) for (var i = 0; i < MaxParticleTypes; i++)
CreateRandomParticleType(); CreateRandomParticleType();
//for (var i = 0; i < MaxParticles; i++)
// CreateRandomParticle();
} }
public void Update() public void Update()
{ {
LastParticlesRemoved.Clear(); LastParticlesRemoved.Clear();
LastParticlesAdded.Clear(); LastParticlesAdded.Clear();
_tasks.Clear();
// update all particles
#if MULTITHREADED
_tasks.Clear();
foreach (var id in _particles.Keys)
_tasks.Add(Task.Factory.StartNew(UpdateParticle, id));
Task.WaitAll(_tasks.ToArray());
#else
foreach (var id in _particles.Keys) foreach (var id in _particles.Keys)
_tasks.Add(Task.Factory.StartNew(UpdateParticle, id)); UpdateParticle(id);
Task.WaitAll(_tasks.ToArray()); #endif
var deletedParticle = false;
foreach (var p in _particles) // used to ensure only one particle is moved per update
var movedParticle = false;
foreach (var particle in _particles.Select(p => p.Value))
{ {
var particle = p.Value; particle.WasTeleportedLast = false;
particle.ScreenWrappedLast = false; if (movedParticle == false && particle.Health == 0f)
if (deletedParticle == false && particle.Health == 0f)
{ {
if (GD.Randf() < 0.2f) if (GD.Randf() < 0.1f)
{ {
particle.Position = GetRandomParticlePosition(); particle.Position = GetRandomParticlePosition();
particle.ScreenWrappedLast = true; particle.WasTeleportedLast = true;
//LastParticlesRemoved.Add(p.Key); particle.Health = 1f;
deletedParticle = true; movedParticle = true;
continue; continue;
} }
} }
var position = particle.Position; var position = particle.Position;
particle.Velocity = particle.Velocity.Clamped(5f); particle.Velocity = particle.Velocity.Clamped(5f);
position += particle.Velocity; position += particle.Velocity;
@ -68,52 +86,48 @@ namespace Particles.ParticleSimulation
if (position.x > SpaceSize.x) if (position.x > SpaceSize.x)
{ {
position.x -= SpaceSize.x; position.x -= SpaceSize.x;
particle.ScreenWrappedLast = true; particle.WasTeleportedLast = true;
} }
else if (position.x < 0) else if (position.x < 0)
{ {
position.x += SpaceSize.x; position.x += SpaceSize.x;
particle.ScreenWrappedLast = true; particle.WasTeleportedLast = true;
} }
if (position.y > SpaceSize.y) if (position.y > SpaceSize.y)
{ {
position.y -= SpaceSize.y; position.y -= SpaceSize.y;
particle.ScreenWrappedLast = true; particle.WasTeleportedLast = true;
} }
else if (position.y < 0) else if (position.y < 0)
{ {
position.y += SpaceSize.y; position.y += SpaceSize.y;
particle.ScreenWrappedLast = true; particle.WasTeleportedLast = true;
} }
particle.AddAverageSpeedValue(particle.Position.DistanceTo(position));
/* /*
if (particle.AverageSpeed < 0.3f) particle.AddAverageSpeedValue(particle.Velocity.Length());
if (particle.AverageSpeed < 0.5f)
particle.Health -= HealthDelta * NegativeHealthMultiplier; particle.Health -= HealthDelta * NegativeHealthMultiplier;
else else
particle.Health += HealthDelta; particle.Health += HealthDelta * PositiveHealthMultiplier;
*/
if (deletedParticle == false && particle.Health == 0f) if (movedParticle == false && particle.Health == 0f)
{ {
if (GD.Randf() < 0.2f) if (GD.Randf() < 0.1f)
{ {
//LastParticlesRemoved.Add(p.Key);
particle.Position = GetRandomParticlePosition(); particle.Position = GetRandomParticlePosition();
particle.ScreenWrappedLast = true; particle.ResetAverageSpeed();
deletedParticle = true; particle.WasTeleportedLast = true;
particle.Health = 1f;
movedParticle = true;
continue; continue;
} }
} }
*/
particle.Position = position; particle.Position = position;
} }
foreach (var id in LastParticlesRemoved)
{
_particles.Remove(id);
}
// ReSharper disable once InvertIf // ReSharper disable once InvertIf
if (_particles.Count < MaxParticles) if (_particles.Count < MaxParticles)
{ {
@ -132,6 +146,13 @@ namespace Particles.ParticleSimulation
} }
} }
// ReSharper disable once UnusedMember.Local
private void RemoveParticle(int id)
{
_particles.Remove(id);
LastParticlesRemoved.Add(id);
}
private void CreateRandomParticleType() private void CreateRandomParticleType()
{ {
var type = new ParticleType() var type = new ParticleType()
@ -192,7 +213,7 @@ namespace Particles.ParticleSimulation
return _particles[id]; return _particles[id];
} }
private Task UpdateParticle(object i) private void UpdateParticle(object i)
{ {
var id = (int)i; var id = (int)i;
var particle1 = _particles[id]; var particle1 = _particles[id];
@ -206,15 +227,17 @@ namespace Particles.ParticleSimulation
var distanceSquared = particle1.Position.DistanceSquaredTo(position); var distanceSquared = particle1.Position.DistanceSquaredTo(position);
if (distanceSquared > (55f * 55f)) if (distanceSquared > (55f * 55f))
continue; continue;
var direction = particle1.Position.DirectionTo(position);
if (distanceSquared < (35f * 35f)) if (distanceSquared < (35f * 35f))
closeCount++; closeCount++;
// collision force // collision force
float distance; float distance;
Vector2 direction;
if (distanceSquared < (ParticleCollisionRadius * ParticleCollisionRadius)) if (distanceSquared < (ParticleCollisionRadius * ParticleCollisionRadius))
{ {
direction = particle1.Position.DirectionTo(position);
distance = particle1.Position.DistanceTo(position); distance = particle1.Position.DistanceTo(position);
var collisionForce = 1f / (0.35f + Mathf.Pow(Mathf.E, -1.15f * (distance - 12f))) - 1f / 0.35f; var collisionForce = 1f / (0.35f + Mathf.Pow(Mathf.E, -1.15f * (distance - 12f))) - 1f / 0.35f;
particle1.Velocity += direction * collisionForce; particle1.Velocity += direction * collisionForce;
@ -225,6 +248,7 @@ namespace Particles.ParticleSimulation
if (props.Force != 0f && distanceSquared >= props.MinRadius * props.MinRadius && if (props.Force != 0f && distanceSquared >= props.MinRadius * props.MinRadius &&
distanceSquared <= props.MaxRadius * props.MaxRadius) distanceSquared <= props.MaxRadius * props.MaxRadius)
{ {
direction = particle1.Position.DirectionTo(position);
distance = particle1.Position.DistanceTo(position); distance = particle1.Position.DistanceTo(position);
var mid = (props.MinRadius + props.MaxRadius) / 2f; var mid = (props.MinRadius + props.MaxRadius) / 2f;
float particleForce; float particleForce;
@ -256,12 +280,10 @@ namespace Particles.ParticleSimulation
} }
} }
if (closeCount > 50) if (closeCount > 70)
particle1.Health -= HealthDelta * NegativeHealthMultiplier; particle1.Health -= HealthDelta * NegativeHealthMultiplier;
else else
particle1.Health += HealthDelta * PositiveHealthMultiplier; particle1.Health += HealthDelta * PositiveHealthMultiplier;
return Task.CompletedTask;
} }
} }
} }

View File

@ -1,7 +1,5 @@
<Project Sdk="Godot.NET.Sdk/3.3.0"> <Project Sdk="Godot.NET.Sdk/3.3.0">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net472</TargetFramework> <TargetFramework>net472</TargetFramework>
<Configurations>Debug;ExportDebug;ExportRelease;Release</Configurations> </PropertyGroup>
<Platforms>AnyCPU</Platforms>
</PropertyGroup>
</Project> </Project>

View File

@ -1,22 +1,19 @@
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2012 # Visual Studio 2012
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Particles", "Particles.csproj", "{E15A1F42-C497-4EDE-8944-84CB055B33CB}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Particles", "Particles.csproj", "{87802628-5B53-4FFD-8ACF-E7227985842E}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
ExportDebug|Any CPU = ExportDebug|Any CPU ExportDebug|Any CPU = ExportDebug|Any CPU
ExportRelease|Any CPU = ExportRelease|Any CPU ExportRelease|Any CPU = ExportRelease|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{E15A1F42-C497-4EDE-8944-84CB055B33CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {87802628-5B53-4FFD-8ACF-E7227985842E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E15A1F42-C497-4EDE-8944-84CB055B33CB}.Debug|Any CPU.Build.0 = Debug|Any CPU {87802628-5B53-4FFD-8ACF-E7227985842E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E15A1F42-C497-4EDE-8944-84CB055B33CB}.ExportDebug|Any CPU.ActiveCfg = ExportDebug|Any CPU {87802628-5B53-4FFD-8ACF-E7227985842E}.ExportDebug|Any CPU.ActiveCfg = ExportDebug|Any CPU
{E15A1F42-C497-4EDE-8944-84CB055B33CB}.ExportDebug|Any CPU.Build.0 = ExportDebug|Any CPU {87802628-5B53-4FFD-8ACF-E7227985842E}.ExportDebug|Any CPU.Build.0 = ExportDebug|Any CPU
{E15A1F42-C497-4EDE-8944-84CB055B33CB}.ExportRelease|Any CPU.ActiveCfg = Debug|Any CPU {87802628-5B53-4FFD-8ACF-E7227985842E}.ExportRelease|Any CPU.ActiveCfg = ExportRelease|Any CPU
{E15A1F42-C497-4EDE-8944-84CB055B33CB}.ExportRelease|Any CPU.Build.0 = Debug|Any CPU {87802628-5B53-4FFD-8ACF-E7227985842E}.ExportRelease|Any CPU.Build.0 = ExportRelease|Any CPU
{E15A1F42-C497-4EDE-8944-84CB055B33CB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E15A1F42-C497-4EDE-8944-84CB055B33CB}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
EndGlobal EndGlobal

View File

@ -1,2 +0,0 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/PencilsConfiguration/ActualSeverity/@EntryValue">INFO</s:String></wpf:ResourceDictionary>

7
default_env.tres Normal file
View File

@ -0,0 +1,7 @@
[gd_resource type="Environment" load_steps=2 format=2]
[sub_resource type="ProceduralSky" id=1]
[resource]
background_mode = 2
background_sky = SubResource( 1 )

BIN
icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

View File

@ -13,26 +13,30 @@ config_version=4
config/name="Particles" config/name="Particles"
run/main_scene="res://Main.tscn" run/main_scene="res://Main.tscn"
config/icon="res://icon.png" config/icon="res://icon.png"
config/windows_native_icon="res://icon.ico"
[display] [display]
window/size/width=1920
window/size/height=1080
window/size/resizable=false window/size/resizable=false
window/size/borderless=true window/size/borderless=true
window/size/fullscreen=true
[input] [input]
quit={
"deadzone": 0.5,
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":0,"physical_scancode":16777217,"unicode":0,"echo":false,"script":null)
]
}
reset={ reset={
"deadzone": 0.5, "deadzone": 0.5,
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":0,"physical_scancode":82,"unicode":0,"echo":false,"script":null) "events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":82,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
] ]
} }
quit={
"deadzone": 0.5,
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777217,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
]
}
[mono]
profiler/enabled=true
[physics] [physics]
@ -45,3 +49,4 @@ quality/driver/driver_name="GLES2"
vram_compression/import_etc=true vram_compression/import_etc=true
vram_compression/import_etc2=false vram_compression/import_etc2=false
environment/default_clear_color=Color( 0, 0, 0, 1 ) environment/default_clear_color=Color( 0, 0, 0, 1 )
environment/default_environment="res://default_env.tres"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -0,0 +1,35 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/particle_noborder.png-2c964d106064b04cd1632e2feec7d5d8.stex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://textures/particle_noborder.png"
dest_files=[ "res://.import/particle_noborder.png-2c964d106064b04cd1632e2feec7d5d8.stex" ]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=0
flags/filter=true
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
process/normal_map_invert_y=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0

BIN
textures/particle_old.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,35 @@
[remap]
importer="texture"
type="StreamTexture"
path="res://.import/particle_old.png-70319fb8cbbe1fe7999a1d4ae575ed1d.stex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://textures/particle_old.png"
dest_files=[ "res://.import/particle_old.png-70319fb8cbbe1fe7999a1d4ae575ed1d.stex" ]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=0
flags/filter=true
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
process/normal_map_invert_y=false
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0