Created
May 6, 2022 22:36
-
-
Save gnysek/8a63910e7e40a7b0205a44bea76ca98b to your computer and use it in GitHub Desktop.
windblown
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Simple particle system with blowing wind effect | |
// Note that this is *not* supposed to be an even vaguely accurate simulation | |
// There are also still a lot of optimisations possible | |
function _effect_windblown_particles() constructor | |
{ | |
param_num_particles = 100; | |
param_particle_spawn_time = 100; // measured in milliseconds | |
param_particle_spawn_all_at_start = 0; | |
param_warmup_frames = 0; | |
param_sprite = _effect_windblown_particles_leaf_sprite; | |
param_particle_mass_min = 0.005; | |
param_particle_mass_max = 0.01; | |
param_particle_start_sprite_scale = 0.25; | |
param_particle_end_sprite_scale = 0.25; | |
param_particle_col_1 = [1.0, 1.0, 1.0, 1.0]; | |
param_particle_col_alt_1 = [1.0, 1.0, 1.0, 1.0]; | |
param_particle_col_2 = [1.0, 1.0, 1.0, 1.0]; | |
param_particle_col_alt_2 = [1.0, 1.0, 1.0, 1.0]; | |
param_particle_col_3 = [1.0, 1.0, 1.0, 1.0]; | |
param_particle_col_alt_3 = [1.0, 1.0, 1.0, 1.0]; | |
param_particle_col_4 = [1.0, 1.0, 1.0, 1.0]; | |
param_particle_col_alt_4 = [1.0, 1.0, 1.0, 1.0]; | |
param_particle_col_enabled_2 = 0; | |
param_particle_col_enabled_3 = 0; | |
param_particle_col_2_pos = 0.33; | |
param_particle_col_3_pos = 0.66; | |
param_particle_initial_velocity_range_x_min = -100; | |
param_particle_initial_velocity_range_x_max = 100; | |
param_particle_initial_velocity_range_y_min = -100; | |
param_particle_initial_velocity_range_y_max = 100; | |
param_particle_initial_rotation_min = 0; | |
param_particle_initial_rotation_max = 360; | |
param_particle_rot_speed_min = -360; | |
param_particle_rot_speed_max = 360; | |
param_particle_lifetime_min = 100; | |
param_particle_lifetime_max = 100; | |
param_particle_update_skip = 1; | |
param_particle_spawn_border_prop = 0.25; | |
param_particle_src_blend = bm_src_alpha; | |
param_particle_dest_blend = bm_inv_src_alpha; | |
param_particle_align_vel = 1; | |
param_trails_only = 0; | |
param_trail_chance = 20; // percentage chance a particle will have a trail | |
param_trail_lifetime_min = 0.1; // trail lifetime in seconds | |
param_trail_lifetime_max = 0.5; // trail lifetime in seconds | |
param_trail_thickness_min = 0.25; | |
param_trail_thickness_max = 1.0; | |
param_trail_col_1 = [1.0, 1.0, 1.0, 0.1]; | |
param_trail_col_alt_1 = [1.0, 1.0, 1.0, 0.25]; | |
param_trail_col_2 = [1.0, 1.0, 1.0, 0.1]; | |
param_trail_col_alt_2 = [1.0, 1.0, 1.0, 0.25]; | |
param_trail_col_3 = [1.0, 1.0, 1.0, 0.1]; | |
param_trail_col_alt_3 = [1.0, 1.0, 1.0, 0.25]; | |
param_trail_col_4 = [1.0, 1.0, 1.0, 0.0]; | |
param_trail_col_alt_4 = [1.0, 1.0, 1.0, 0.0]; | |
param_trail_col_enabled_2 = 1; | |
param_trail_col_enabled_3 = 0; | |
param_trail_col_2_pos = 0.5; | |
param_trail_col_3_pos = 0.66; | |
param_trail_min_segment_length = 20; | |
param_trail_src_blend = bm_src_alpha; | |
param_trail_dest_blend = bm_inv_src_alpha; | |
param_force_grid_sizex = 8; | |
param_force_grid_sizey = 8; | |
param_wind_vector_x = -4; | |
param_wind_vector_y = -1; | |
param_num_blowers = 3; | |
param_blower_size_min = 0.2; | |
param_blower_size_max = 0.6; | |
param_blower_speed_min = 0.2; // proportion of screen crossed in a second | |
param_blower_speed_max = 0.5; // proportion of the screen crossed in a second | |
param_blower_rot_speed_min = -180; | |
param_blower_rot_speed_max = 180; | |
param_blower_force_min = 5; | |
param_blower_force_max = 15; | |
param_blower_camvec_scale = -1.0; | |
air_density = 0.01; | |
param_dragcoeff = 1.0; | |
param_grav_accel = 100.0; | |
param_debug_grid = 0; | |
debug_grid_alpha = 0.25; | |
debug_force_scale = 10.0; | |
particle_scale_compensation = 1.0; | |
time_since_last_particle = 0.0; | |
trails_use_trilists = 0; | |
particle_col = array_create(4); | |
particle_alpha = array_create(4); | |
particle_col_alt = array_create(4); | |
particle_alpha_alt = array_create(4); | |
particle_col_pos = array_create(4); | |
particle_col_dist = array_create(3); | |
trail_col = array_create(4); | |
trail_alpha = array_create(4); | |
trail_col_alt = array_create(4); | |
trail_alpha_alt = array_create(4); | |
trail_col_pos = array_create(4); | |
trail_col_dist = array_create(3); | |
update_colours = function() | |
{ | |
// Particle colours | |
particle_col[0] = make_colour_rgb(param_particle_col_1[0] * 255, param_particle_col_1[1] * 255, param_particle_col_1[2] * 255); | |
particle_col[1] = make_colour_rgb(param_particle_col_2[0] * 255, param_particle_col_2[1] * 255, param_particle_col_2[2] * 255); | |
particle_col[2] = make_colour_rgb(param_particle_col_3[0] * 255, param_particle_col_3[1] * 255, param_particle_col_3[2] * 255); | |
particle_col[3] = make_colour_rgb(param_particle_col_4[0] * 255, param_particle_col_4[1] * 255, param_particle_col_4[2] * 255); | |
particle_col_alt[0] = make_colour_rgb(param_particle_col_alt_1[0] * 255, param_particle_col_alt_1[1] * 255, param_particle_col_alt_1[2] * 255); | |
particle_col_alt[1] = make_colour_rgb(param_particle_col_alt_2[0] * 255, param_particle_col_alt_2[1] * 255, param_particle_col_alt_2[2] * 255); | |
particle_col_alt[2] = make_colour_rgb(param_particle_col_alt_3[0] * 255, param_particle_col_alt_3[1] * 255, param_particle_col_alt_3[2] * 255); | |
particle_col_alt[3] = make_colour_rgb(param_particle_col_alt_4[0] * 255, param_particle_col_alt_4[1] * 255, param_particle_col_alt_4[2] * 255); | |
particle_alpha[0] = param_particle_col_1[3]; | |
particle_alpha[1] = param_particle_col_2[3]; | |
particle_alpha[2] = param_particle_col_3[3]; | |
particle_alpha[3] = param_particle_col_4[3]; | |
particle_alpha_alt[0] = param_particle_col_alt_1[3]; | |
particle_alpha_alt[1] = param_particle_col_alt_2[3]; | |
particle_alpha_alt[2] = param_particle_col_alt_3[3]; | |
particle_alpha_alt[3] = param_particle_col_alt_4[3]; | |
particle_col_pos[0] = 0.0; | |
particle_col_pos[1] = param_particle_col_2_pos; | |
particle_col_pos[2] = param_particle_col_3_pos; | |
particle_col_pos[3] = 1.0; | |
if (param_particle_col_enabled_3 == 0) | |
{ | |
particle_col[2] = particle_col[3]; | |
particle_col_alt[2] = particle_col_alt[3]; | |
particle_alpha[2] = particle_alpha[3]; | |
particle_alpha_alt[2] = particle_alpha_alt[3]; | |
particle_col_pos[2] = 1.0; | |
} | |
if (param_particle_col_enabled_2 == 0) | |
{ | |
particle_col[1] = particle_col[2]; | |
particle_col_alt[1] = particle_col_alt[2]; | |
particle_alpha[1] = particle_alpha[2]; | |
particle_alpha_alt[1] = particle_alpha_alt[2]; | |
particle_col_pos[1] = particle_col_pos[2]; | |
} | |
particle_col_dist[0] = particle_col_pos[1] - particle_col_pos[0]; | |
particle_col_dist[1] = particle_col_pos[2] - particle_col_pos[1]; | |
particle_col_dist[2] = particle_col_pos[3] - particle_col_pos[2]; | |
// Trail colours | |
trail_col[0] = make_colour_rgb(param_trail_col_1[0] * 255, param_trail_col_1[1] * 255, param_trail_col_1[2] * 255); | |
trail_col[1] = make_colour_rgb(param_trail_col_2[0] * 255, param_trail_col_2[1] * 255, param_trail_col_2[2] * 255); | |
trail_col[2] = make_colour_rgb(param_trail_col_3[0] * 255, param_trail_col_3[1] * 255, param_trail_col_3[2] * 255); | |
trail_col[3] = make_colour_rgb(param_trail_col_4[0] * 255, param_trail_col_4[1] * 255, param_trail_col_4[2] * 255); | |
trail_col_alt[0] = make_colour_rgb(param_trail_col_alt_1[0] * 255, param_trail_col_alt_1[1] * 255, param_trail_col_alt_1[2] * 255); | |
trail_col_alt[1] = make_colour_rgb(param_trail_col_alt_2[0] * 255, param_trail_col_alt_2[1] * 255, param_trail_col_alt_2[2] * 255); | |
trail_col_alt[2] = make_colour_rgb(param_trail_col_alt_3[0] * 255, param_trail_col_alt_3[1] * 255, param_trail_col_alt_3[2] * 255); | |
trail_col_alt[3] = make_colour_rgb(param_trail_col_alt_4[0] * 255, param_trail_col_alt_4[1] * 255, param_trail_col_alt_4[2] * 255); | |
trail_alpha[0] = param_trail_col_1[3]; | |
trail_alpha[1] = param_trail_col_2[3]; | |
trail_alpha[2] = param_trail_col_3[3]; | |
trail_alpha[3] = param_trail_col_4[3]; | |
trail_alpha_alt[0] = param_trail_col_alt_1[3]; | |
trail_alpha_alt[1] = param_trail_col_alt_2[3]; | |
trail_alpha_alt[2] = param_trail_col_alt_3[3]; | |
trail_alpha_alt[3] = param_trail_col_alt_4[3]; | |
trail_col_pos[0] = 0.0; | |
trail_col_pos[1] = param_trail_col_2_pos; | |
trail_col_pos[2] = param_trail_col_3_pos; | |
trail_col_pos[3] = 1.0; | |
if (param_trail_col_enabled_3 == 0) | |
{ | |
trail_col[2] = trail_col[3]; | |
trail_col_alt[2] = trail_col_alt[3]; | |
trail_alpha[2] = trail_alpha[3]; | |
trail_alpha_alt[2] = trail_alpha_alt[3]; | |
trail_col_pos[2] = 1.0; | |
} | |
if (param_trail_col_enabled_2 == 0) | |
{ | |
trail_col[1] = trail_col[2]; | |
trail_col_alt[1] = trail_col_alt[2]; | |
trail_alpha[1] = trail_alpha[2]; | |
trail_alpha_alt[1] = trail_alpha_alt[2]; | |
trail_col_pos[1] = trail_col_pos[2]; | |
} | |
trail_col_dist[0] = trail_col_pos[1] - trail_col_pos[0]; | |
trail_col_dist[1] = trail_col_pos[2] - trail_col_pos[1]; | |
trail_col_dist[2] = trail_col_pos[3] - trail_col_pos[2]; | |
} | |
reset = function() | |
{ | |
frame = 0; | |
particles = array_create(0); | |
num_particles = 0; | |
trail_min_segment_length_sq = param_trail_min_segment_length * param_trail_min_segment_length; | |
force_grid_sizex = -1; | |
force_grid_sizey = -1; | |
force_grid = -1; | |
force_grid_centrex = 0; // this is the room position the force grid is based at | |
force_grid_centrey = 0; | |
force_grid_offsetx = 0; // this is the position in the grid array we start at (circular buffer style) | |
force_grid_offsety = 0; | |
grid_margin = 1; // this is amount we want the force grid to overhang on each side | |
last_view_centrex = 0; | |
last_view_centrey = 0; | |
blowers = array_create(0); | |
num_blowers = 0; | |
trails = array_create(0); | |
num_trails = 0; | |
views_minx = 0; | |
views_maxx = 0; | |
views_miny = 0; | |
views_maxx = 0; | |
views_centrex = 0; | |
views_centrey = 0; | |
update_colours(); | |
sprwidth = sprite_get_width(param_sprite); | |
sprheight = sprite_get_height(param_sprite); | |
particle_scale_compensation = 1.0; | |
} | |
cleanup = function() | |
{ | |
} | |
spawn_blower = function(_minx, _maxx, _miny, _maxy) | |
{ | |
var blower = | |
{ | |
velx:((_maxx - _minx) * random_range(param_blower_speed_min, param_blower_speed_max)), | |
posy:random_range(_miny, _maxy), | |
size:(random_range(param_blower_size_min, param_blower_size_max) * (_maxx - _minx)), | |
rotspeed:random_range((param_blower_rot_speed_min / 180.0) * pi, (param_blower_rot_speed_max / 180.0) * pi), | |
rot:random_range(0, pi * 2.0), | |
force:random_range(param_blower_force_min, param_blower_force_max) | |
}; | |
if (random(100) < 50.0) | |
blower.velx = -blower.velx; | |
if (blower.velx < 0) | |
blower.posx = _maxx + blower.size; | |
else | |
blower.posx = _minx - blower.size; | |
return blower; | |
} | |
spawn_trail = function(_lifetime, _spritesize) | |
{ | |
if (array_length(trails) <= num_trails) | |
{ | |
array_resize(trails, num_trails + 1); | |
} | |
var lifetime_in_frames = _lifetime * game_get_speed(gamespeed_fps); | |
var num_segs = ceil(lifetime_in_frames) + 1; // currently just assume one segment per frame (add on an extra segment so we get a full alpha fade-out of the last segment) | |
var trail = | |
{ | |
tail_segment:0, | |
head_segment:0, | |
lifetime:lifetime_in_frames, | |
total_num_segs:num_segs, | |
halfwidth:random_range(param_trail_thickness_min, param_trail_thickness_max) * _spritesize * 0.5, | |
num_segs:0 | |
}; | |
trail.posx = array_create(num_segs); | |
trail.posy = array_create(num_segs); | |
trail.pt1_posx = array_create(num_segs); | |
trail.pt1_posy = array_create(num_segs); | |
trail.pt2_posx = array_create(num_segs); | |
trail.pt2_posy = array_create(num_segs); | |
trail.frame = array_create(num_segs); // only really need this if we're not adding a segment every frame | |
trail.alphascale = 1.0; // can be modified by the particle that drives this trail in order to fade it out | |
var colblend = random_range(0.0, 1.0); | |
trail.cols = array_create(4); | |
trail.alphas = array_create(4); | |
trail.cols[0] = merge_colour(trail_col[0], trail_col_alt[0], colblend); | |
trail.cols[1] = merge_colour(trail_col[1], trail_col_alt[1], colblend); | |
trail.cols[2] = merge_colour(trail_col[2], trail_col_alt[2], colblend); | |
trail.cols[3] = merge_colour(trail_col[3], trail_col_alt[3], colblend); | |
trail.alphas[0] = lerp(trail_alpha[0], trail_alpha_alt[0], colblend); | |
trail.alphas[1] = lerp(trail_alpha[1], trail_alpha_alt[1], colblend); | |
trail.alphas[2] = lerp(trail_alpha[2], trail_alpha_alt[2], colblend); | |
trail.alphas[3] = lerp(trail_alpha[3], trail_alpha_alt[3], colblend); | |
trails[num_trails] = trail; | |
num_trails++; | |
return trail; | |
} | |
add_trail_segment = function(_trail, _posx, _posy) | |
{ | |
var totalnumsegs = _trail.total_num_segs; | |
if (totalnumsegs < 2) | |
{ | |
show_debug_message("Trail has less than two segments and is unusable."); | |
return; | |
} | |
if (_trail.num_segs == 0) | |
{ | |
var headseg = _trail.head_segment; | |
// Duplicate first segment | |
_trail.posx[headseg] = _posx; | |
_trail.posy[headseg] = _posy; | |
_trail.frame[headseg] = frame; | |
_trail.pt1_posx[headseg] = 0.0; | |
_trail.pt1_posy[headseg] = 0.0; | |
_trail.pt2_posx[headseg] = 0.0; | |
_trail.pt2_posy[headseg] = 0.0; | |
headseg = (headseg + 1) % totalnumsegs; | |
_trail.posx[headseg] = _posx; | |
_trail.posy[headseg] = _posy; | |
_trail.frame[headseg] = frame; | |
_trail.pt1_posx[headseg] = 0.0; | |
_trail.pt1_posy[headseg] = 0.0; | |
_trail.pt2_posx[headseg] = 0.0; | |
_trail.pt2_posy[headseg] = 0.0; | |
headseg = (headseg + 1) % totalnumsegs; | |
_trail.head_segment = headseg; | |
_trail.num_segs = 2; | |
} | |
else | |
{ | |
var headseg = (_trail.head_segment + (totalnumsegs - 1)) % totalnumsegs; // update previous segment | |
_trail.posx[headseg] = _posx; | |
_trail.posy[headseg] = _posy; | |
_trail.frame[headseg] = frame; | |
var totalnumsegs = _trail.total_num_segs; | |
var sqlength = 0.0; | |
var prevseg = (headseg + (totalnumsegs - 1)) % totalnumsegs; | |
var diffx = _posx - _trail.posx[prevseg]; | |
var diffy = _posy - _trail.posy[prevseg]; | |
var perpvecx, perpvecy; | |
sqlength = (diffx * diffx) + (diffy * diffy); | |
if (sqlength > 0.0) | |
{ | |
var length = sqrt(sqlength); | |
perpvecx = -(diffy / length) * _trail.halfwidth; | |
perpvecy = (diffx / length) * _trail.halfwidth; | |
} | |
else | |
{ | |
perpvecx = _trail.halfwidth; | |
perpvecy = 0.0; | |
} | |
var pt1_posx = _posx - perpvecx; | |
var pt1_posy = _posy - perpvecy; | |
var pt2_posx = _posx + perpvecx; | |
var pt2_posy = _posy + perpvecy; | |
_trail.pt1_posx[headseg] = pt1_posx; | |
_trail.pt1_posy[headseg] = pt1_posy; | |
_trail.pt2_posx[headseg] = pt2_posx; | |
_trail.pt2_posy[headseg] = pt2_posy; | |
if (_trail.num_segs == 2) | |
{ | |
// If we only have two points in the trail, set the first points perpendicular vectors to the same as the second point | |
_trail.pt1_posx[prevseg] = _trail.posx[prevseg] - perpvecx; | |
_trail.pt1_posy[prevseg] = _trail.posy[prevseg] - perpvecy; | |
_trail.pt2_posx[prevseg] = _trail.posx[prevseg] + perpvecx; | |
_trail.pt2_posy[prevseg] = _trail.posy[prevseg] + perpvecy; | |
} | |
// Ideally we would adjust the offset vectors of the previous point to be perpendicular to the average of the segment vectors | |
// on either side of it but that's probably not noticeable in most cases | |
if (sqlength >= trail_min_segment_length_sq) | |
{ | |
// Only move the head along if the difference between this point and the last is bigger than our threshold | |
var currheadseg = _trail.head_segment; | |
// Copy segment info to current head | |
_trail.posx[currheadseg] = _posx; | |
_trail.posy[currheadseg] = _posy; | |
_trail.frame[currheadseg] = frame; | |
_trail.pt1_posx[currheadseg] = pt1_posx; | |
_trail.pt1_posy[currheadseg] = pt1_posy; | |
_trail.pt2_posx[currheadseg] = pt2_posx; | |
_trail.pt2_posy[currheadseg] = pt2_posy; | |
var nextheadseg = (currheadseg + 1) % totalnumsegs; | |
_trail.head_segment = nextheadseg; | |
if (nextheadseg == _trail.tail_segment) | |
{ | |
// Could resize here, but for the moment just drop the last segment | |
_trail.tail_segment = (_trail.tail_segment + 1) % totalnumsegs; | |
} | |
else | |
{ | |
_trail.num_segs++; | |
} | |
} | |
} | |
} | |
update_trails = function() | |
{ | |
var i; | |
for (i = 0; i < num_trails;) | |
{ | |
var trail = trails[i]; | |
var oldestvalidframe = frame - trail.lifetime; | |
var totalnumsegs = trail.total_num_segs; | |
var trailseg = trail.tail_segment; | |
if (trail.head_segment == trailseg) | |
{ | |
// trail has no segments so bin it | |
trails[i] = trails[num_trails - 1]; | |
num_trails--; | |
} | |
else | |
{ | |
// We only want to remove a segment if the *next* one is too old | |
// This preserves segments that are transition points between being alive and dead | |
// so we get a smooth alpha fade-out | |
var nexttrailseg = (trailseg + 1) % totalnumsegs; | |
while((trail.head_segment != nexttrailseg) && (trail.frame[nexttrailseg] < oldestvalidframe)) | |
{ | |
trailseg = nexttrailseg; | |
nexttrailseg = (nexttrailseg + 1) % totalnumsegs; | |
trail.num_segs--; | |
} | |
var latesttrailseg = (trail.head_segment + (totalnumsegs - 1)) % totalnumsegs; | |
if ((trail.frame[latesttrailseg] < oldestvalidframe)) // if the latest segment is older than the threshold, bin the trail | |
{ | |
// trail has run out of segments so bin it | |
trails[i] = trails[num_trails - 1]; | |
num_trails--; | |
} | |
else | |
{ | |
trail.tail_segment = trailseg; | |
i++; | |
} | |
} | |
} | |
} | |
draw_trails = function() | |
{ | |
var i; | |
gpu_set_blendmode_ext(param_trail_src_blend, param_trail_dest_blend); | |
for (i = 0; i < num_trails; i++) | |
{ | |
var trail = trails[i]; | |
var totalnumsegs = trail.total_num_segs; | |
if (trail.num_segs < 2) | |
continue; // we need at least two active segments | |
var oldestvalidframe = trail.lifetime + frame; | |
var cols = trail.cols; | |
var alphas = trail.alphas; | |
var alphascale = trail.alphascale; | |
var lifetime = trail.lifetime; | |
if (trails_use_trilists) | |
{ | |
// In tests this was considerably slower, despite using less draw calls | |
// The GML overhead outways any benefit - still various optimisations possible here though | |
draw_primitive_begin(pr_trianglelist); | |
var trailseg = trail.tail_segment; | |
var headseg = trail.head_segment; | |
var col = cols[3]; | |
var last_pt1_posx = trail.pt1_posx[trailseg]; | |
var last_pt1_posy = trail.pt1_posy[trailseg]; | |
var last_pt2_posx = trail.pt2_posx[trailseg]; | |
var last_pt2_posy = trail.pt2_posy[trailseg]; | |
var lastcol = col; | |
var lastalpha = alphas[3]; | |
// This could be optimised | |
var lifeprop = (frame - trail.frame[trailseg]) / lifetime; | |
lifeprop = clamp(lifeprop, 0.0, 1.0); | |
var colprop = lifeprop; | |
var colzone = 0; | |
colprop -= trail_col_dist[0]; | |
while((colprop > 0.0) && (colzone < 2)) | |
{ | |
colzone++ | |
colprop -= trail_col_dist[colzone]; | |
} | |
var col_dist = trail_col_dist[colzone]; | |
if (col_dist <= 0.0) | |
colprop = 0.0; | |
else | |
colprop = (lifeprop - trail_col_pos[colzone]) / col_dist; | |
lastcol = merge_colour(cols[colzone], cols[colzone+1], colprop); | |
lastalpha = lerp(alphas[colzone], alphas[colzone+1], colprop) * alphascale; | |
trailseg = (trailseg + 1) % totalnumsegs; | |
while(headseg != trailseg) | |
{ | |
// This could be optimised | |
lifeprop = (frame - trail.frame[trailseg]) / lifetime; | |
lifeprop = clamp(lifeprop, 0.0, 1.0); | |
colprop = lifeprop; | |
colzone = 0; | |
colprop -= trail_col_dist[0]; | |
while((colprop > 0.0) && (colzone < 2)) | |
{ | |
colzone++ | |
colprop -= trail_col_dist[colzone]; | |
} | |
col_dist = trail_col_dist[colzone]; | |
if (col_dist <= 0.0) | |
colprop = 0.0; | |
else | |
colprop = (lifeprop - trail_col_pos[colzone]) / col_dist; | |
var col = merge_colour(cols[colzone], cols[colzone+1], colprop); | |
var alpha = lerp(alphas[colzone], alphas[colzone+1], colprop) * alphascale; | |
draw_vertex_colour(last_pt1_posx, last_pt1_posy, lastcol, lastalpha); | |
draw_vertex_colour(last_pt2_posx, last_pt2_posy, lastcol, lastalpha); | |
draw_vertex_colour(trail.pt1_posx[trailseg], trail.pt1_posy[trailseg], col, alpha); | |
draw_vertex_colour(trail.pt1_posx[trailseg], trail.pt1_posy[trailseg], col, alpha); | |
draw_vertex_colour(last_pt2_posx, last_pt2_posy, lastcol, lastalpha); | |
draw_vertex_colour(trail.pt2_posx[trailseg], trail.pt2_posy[trailseg], col, alpha); | |
last_pt1_posx = trail.pt1_posx[trailseg]; | |
last_pt1_posy = trail.pt1_posy[trailseg]; | |
last_pt2_posx = trail.pt2_posx[trailseg]; | |
last_pt2_posy = trail.pt2_posy[trailseg]; | |
lastcol = col; | |
lastalpha = alpha; | |
trailseg = (trailseg + 1) % totalnumsegs; | |
} | |
draw_primitive_end(); | |
} | |
else | |
{ | |
// TODO: Look at joining strips with degenerate tris to reduce draw calls | |
draw_primitive_begin(pr_trianglestrip); | |
var trailseg = trail.tail_segment; | |
var headseg = trail.head_segment; | |
while(headseg != trailseg) | |
{ | |
var lifeprop = (frame - trail.frame[trailseg]) / lifetime; | |
lifeprop = clamp(lifeprop, 0.0, 1.0); | |
var colprop = lifeprop; | |
var colzone = 0; | |
colprop -= trail_col_dist[0]; | |
while((colprop > 0.0) && (colzone < 2)) | |
{ | |
colzone++ | |
colprop -= trail_col_dist[colzone]; | |
} | |
var col_dist = trail_col_dist[colzone]; | |
if (col_dist <= 0.0) | |
colprop = 0.0; | |
else | |
colprop = (lifeprop - trail_col_pos[colzone]) / col_dist; | |
var col = merge_colour(cols[colzone], cols[colzone+1], colprop); | |
var alpha = lerp(alphas[colzone], alphas[colzone+1], colprop) * alphascale; | |
draw_vertex_colour(trail.pt1_posx[trailseg], trail.pt1_posy[trailseg], col, alpha); | |
draw_vertex_colour(trail.pt2_posx[trailseg], trail.pt2_posy[trailseg], col, alpha); | |
trailseg = (trailseg + 1) % totalnumsegs; | |
} | |
draw_primitive_end(); | |
} | |
} | |
} | |
setup_particle = function(_spawnxmin, _spawnxmax, _spawnymin, _spawnymax, _spawnweights, _centrex, _centrey, _view_velx, _view_vely, _gamespeed, _spawn_in_one_zone) | |
{ | |
var particle = | |
{ | |
velx:random_range(param_particle_initial_velocity_range_x_min, param_particle_initial_velocity_range_x_max), | |
vely:random_range(param_particle_initial_velocity_range_y_min, param_particle_initial_velocity_range_y_max), | |
rot:random_range(param_particle_initial_rotation_min, param_particle_initial_rotation_max), | |
rotspeed:random_range(param_particle_rot_speed_min, param_particle_rot_speed_max), | |
subimage:irandom_range(0, sprite_get_number(param_sprite) - 1), | |
mass:max(random_range(param_particle_mass_min, param_particle_mass_max), 0.00001), | |
lifetime:(random_range(param_particle_lifetime_min, param_particle_lifetime_max) * _gamespeed), // lifetime in frames | |
spawnframe:frame | |
}; | |
if (_spawn_in_one_zone) | |
{ | |
// In this mode, _spawnxmin etc are single values rather than an array | |
particle.posx = random_range(_spawnxmin, _spawnxmax); | |
particle.posy = random_range(_spawnymin, _spawnymax); | |
particle.lastposx = particle.posx; | |
particle.lastposy = particle.posy; | |
} | |
else | |
{ | |
// Use weighted zone selection so we don't spawn the same amount of particles in smaller zones as larger ones | |
// The order of the zones is: | |
// +---+---+---+ | |
// | 0 | 1 | 2 | | |
// +---+---+---+ | |
// | 3 | | 4 | | |
// +---+---+---+ | |
// | 5 | 6 | 7 | | |
// +---+---+---+ | |
var zonerand = random_range(0, 0.9999); | |
var zone = 0; | |
zonerand -= _spawnweights[zone]; | |
while((zonerand > 0) && (zone < 7)) | |
{ | |
zone++; | |
zonerand -= _spawnweights[zone]; | |
} | |
particle.posx = random_range(_spawnxmin[zone], _spawnxmax[zone]); | |
particle.posy = random_range(_spawnymin[zone], _spawnymax[zone]); | |
particle.lastposx = particle.posx; | |
particle.lastposy = particle.posy; | |
var view_rel_velx = particle.velx - _view_velx; | |
var view_rel_vely = particle.vely - _view_vely; | |
// Make sure the particles are actually on a trajectory that will move then into view | |
// If they are moving in the wrong direction, mirror their position across the centre point | |
// We could select the correct zone in the first place but that is probably slower as the zones | |
// aren't arranged in a regular order | |
// See above for zone indicies | |
if ((((particle.posx - _spawnxmax[0]) < 0) && (view_rel_velx < 0)) || (((particle.posx - _spawnxmin[2]) > 0) && (view_rel_velx > 0))) | |
particle.posx = _centrex + (_centrex - particle.posx); // flip particle position around centre | |
if ((((particle.posy - _spawnymax[0]) < 0) && (view_rel_vely < 0)) || (((particle.posy - _spawnymin[5]) > 0) && (view_rel_vely > 0))) | |
particle.posy = _centrey + (_centrey - particle.posy); // flip particle position around centre | |
} | |
var colblend = random_range(0.0, 1.0); | |
particle.cols = array_create(4); | |
particle.alphas = array_create(4); | |
particle.cols[0] = merge_colour(particle_col[0], particle_col_alt[0], colblend); | |
particle.cols[1] = merge_colour(particle_col[1], particle_col_alt[1], colblend); | |
particle.cols[2] = merge_colour(particle_col[2], particle_col_alt[2], colblend); | |
particle.cols[3] = merge_colour(particle_col[3], particle_col_alt[3], colblend); | |
particle.alphas[0] = lerp(particle_alpha[0], particle_alpha_alt[0], colblend); | |
particle.alphas[1] = lerp(particle_alpha[1], particle_alpha_alt[1], colblend); | |
particle.alphas[2] = lerp(particle_alpha[2], particle_alpha_alt[2], colblend); | |
particle.alphas[3] = lerp(particle_alpha[3], particle_alpha_alt[3], colblend); | |
if (random(100) < param_trail_chance) | |
{ | |
var spritesize = min(sprwidth, sprheight) * particle.mass * param_particle_start_sprite_scale * particle_scale_compensation; | |
particle.trail = spawn_trail(random_range(param_trail_lifetime_min, param_trail_lifetime_max), spritesize); | |
add_trail_segment(particle.trail, particle.posx, particle.posy); | |
} | |
else | |
{ | |
particle.trail = undefined; | |
} | |
return particle; | |
} | |
draw_particles = function() | |
{ | |
gpu_set_blendmode_ext(param_particle_src_blend, param_particle_dest_blend); | |
var i; | |
for(i = 0; i < num_particles; i++) | |
{ | |
var particle = particles[i]; | |
var lifeprop = (frame - particle.spawnframe) / particle.lifetime; | |
lifeprop = clamp(lifeprop, 0.0, 1.0); | |
// Get the interpolated colour and alpha values from our piecewise-linear curve | |
// It may be worth converting this to a regularly spaced list of points | |
// which would be quicker to evaluate at the cost of some inaccuracy | |
var colprop = lifeprop; | |
var colzone = 0; | |
colprop -= particle_col_dist[0]; | |
while((colprop > 0.0) && (colzone < 2)) | |
{ | |
colzone++ | |
colprop -= particle_col_dist[colzone]; | |
} | |
var col_dist = particle_col_dist[colzone]; | |
if (col_dist <= 0.0) | |
colprop = 0.0; | |
else | |
colprop = (lifeprop - particle_col_pos[colzone]) / col_dist; | |
var cols = particle.cols; | |
var alphas = particle.alphas; | |
var spritecol = merge_colour(cols[colzone], cols[colzone+1], colprop); | |
var spritealpha = lerp(alphas[colzone], alphas[colzone+1], colprop); | |
if (particle.trail != undefined) | |
{ | |
particle.trail.alphascale = spritealpha; | |
} | |
if (param_trails_only == 0) | |
{ | |
var rot = 0.0; | |
if ((param_particle_align_vel > 0) && (particle.lastposx != particle.posx) && (particle.lastposy != particle.posy)) | |
{ | |
var diffx = particle.posx - particle.lastposx; | |
var diffy = particle.posy - particle.lastposy; | |
var length = sqrt((diffx * diffx) + (diffy * diffy)); | |
diffy /= length; | |
rot = (arccos(-diffy) / pi) * 180.0; | |
if (diffx > 0) | |
rot = -rot; | |
} | |
var spritescale = particle.mass * particle_scale_compensation * lerp(param_particle_start_sprite_scale, param_particle_end_sprite_scale, lifeprop); | |
draw_sprite_ext(param_sprite, particle.subimage, particle.posx, particle.posy, spritescale, spritescale, particle.rot + rot, spritecol, spritealpha); | |
} | |
} | |
} | |
step = function() | |
{ | |
var i, j, k; | |
var gamespeed = game_get_speed(gamespeed_fps); | |
var timedelta = 1.0 / gamespeed; | |
// update colour values | |
update_colours(); | |
var minx = 0, maxx = 0, miny = 0, maxy = 0; | |
if (view_enabled) | |
{ | |
// Get maximal bounding box of current views | |
// Ideally we'd do this based on the actual matrices so we'd handle perspective cameras too | |
// But that requires that we know what depth we're drawing at, which isn't practical from Step | |
var first = true; | |
for(i = 0; i < 8; i++) | |
{ | |
if (view_visible[i]) | |
{ | |
var cam = view_camera[i]; | |
var cam_minx = camera_get_view_x(cam); | |
var cam_miny = camera_get_view_y(cam); | |
var cam_maxx = cam_minx + camera_get_view_width(cam); | |
var cam_maxy = cam_miny + camera_get_view_height(cam); | |
if (first) | |
{ | |
first = false; | |
minx = cam_minx; | |
miny = cam_miny; | |
maxx = cam_maxx; | |
maxy = cam_maxy; | |
} | |
else | |
{ | |
minx = min(minx, cam_minx); | |
miny = min(miny, cam_miny); | |
maxx = max(maxx, cam_maxx); | |
maxy = max(maxy, cam_maxy); | |
} | |
} | |
} | |
} | |
else | |
{ | |
minx = 0; | |
miny = 0; | |
maxx = room_width; | |
maxy = room_height; | |
} | |
var centrex = (minx + maxx) / 2.0; | |
var centrey = (miny + maxy) / 2.0; | |
views_minx = minx; | |
views_maxx = maxx; | |
views_miny = miny; | |
views_maxy = maxy; | |
views_centrex = centrex; | |
views_centrey = centrey; | |
if (frame == 0) | |
{ | |
last_view_centrex = centrex; | |
last_view_centrey = centrey; | |
} | |
// Get view movement speed in pixels-per-second | |
var view_velx = centrex - last_view_centrex; | |
var view_vely = centrey - last_view_centrey; | |
if (timedelta != 0) | |
{ | |
view_velx /= timedelta; | |
view_vely /= timedelta; | |
} | |
last_view_centrex = centrex; | |
last_view_centrey = centrey; | |
var floored_param_force_grid_size_x = floor(param_force_grid_sizex); | |
var floored_param_force_grid_size_y = floor(param_force_grid_sizey); | |
var cellsizex = (maxx - minx) / (floored_param_force_grid_size_x - (grid_margin * 2)); | |
var cellsizey = (maxy - miny) / (floored_param_force_grid_size_y - (grid_margin * 2)); | |
if ((force_grid_sizex != floored_param_force_grid_size_x) || | |
(force_grid_sizey != floored_param_force_grid_size_y)) | |
{ | |
force_grid_sizex = floored_param_force_grid_size_x; | |
force_grid_sizey = floored_param_force_grid_size_y; | |
force_grid = array_create(force_grid_sizey * force_grid_sizex * 2); | |
force_grid_centrex = centrex; | |
force_grid_centrey = centrey; | |
} | |
var celldiffx = floor((centrex - force_grid_centrex) / cellsizex); | |
var celldiffy = floor((centrey - force_grid_centrey) / cellsizey); | |
if (celldiffx > 0) | |
{ | |
force_grid_offsetx += celldiffx; | |
force_grid_offsetx = force_grid_offsetx % force_grid_sizex; | |
force_grid_centrex += celldiffx * cellsizex; | |
} | |
else if (celldiffx < 0) | |
{ | |
var celldiff = -celldiffx; | |
celldiff = celldiff % force_grid_sizex; | |
force_grid_offsetx += (force_grid_sizex - celldiff); | |
force_grid_offsetx = force_grid_offsetx % force_grid_sizex; | |
force_grid_centrex += celldiffx * cellsizex; | |
} | |
if (celldiffy > 0) | |
{ | |
force_grid_offsety += celldiffy; | |
force_grid_offsety = force_grid_offsety % force_grid_sizey; | |
force_grid_centrey += celldiffy * cellsizey; | |
} | |
else if (celldiffy < 0) | |
{ | |
var celldiff = -celldiffy; | |
celldiff = celldiff % force_grid_sizey; | |
force_grid_offsety += (force_grid_sizey - celldiff); | |
force_grid_offsety = force_grid_offsety % force_grid_sizey; | |
force_grid_centrey += celldiffy * cellsizey; | |
} | |
var grid_basex = force_grid_centrex - ((force_grid_sizex * cellsizex) * 0.5); | |
var grid_basey = force_grid_centrey - ((force_grid_sizey * cellsizey) * 0.5); | |
var floored_param_num_blowers = floor(param_num_blowers); | |
if (num_blowers < floored_param_num_blowers) | |
{ | |
if (array_length(blowers) < floored_param_num_blowers) | |
{ | |
array_resize(blowers, floored_param_num_blowers); | |
} | |
for(i = num_blowers; i < floored_param_num_blowers; i++) | |
{ | |
blowers[i] = spawn_blower(minx, maxx, miny, maxy); | |
} | |
num_blowers = floored_param_num_blowers; | |
} | |
for(i = 0; i < num_blowers;) | |
{ | |
var blower = blowers[i]; | |
blower.posx += (blower.velx - (view_velx * param_blower_camvec_scale)) * timedelta; | |
blower.rot += (blower.rotspeed * timedelta); | |
var respawn = false; | |
if (((blower.velx < 0) && ((blower.posx + blower.size) < minx)) || | |
((blower.velx > 0) && ((blower.posx - blower.size) > maxx)) || | |
((blower.posy + blower.size) < miny) || | |
((blower.posy - blower.size) > maxy)) | |
{ | |
respawn = true; | |
} | |
if (respawn && (num_blowers > floored_param_num_blowers)) | |
{ | |
// remove blower | |
blowers[i] = blowers[num_blowers - 1]; | |
num_blowers--; | |
} | |
else if (respawn) | |
{ | |
blowers[i] = spawn_blower(minx, maxx, miny, maxy); | |
i++; | |
} | |
else | |
{ | |
i++; | |
} | |
} | |
for(i = 0; i < num_blowers; i++) | |
{ | |
var blower = blowers[i]; | |
blower.vecx = sin(blower.rot) * blower.force; | |
blower.vecy = cos(blower.rot) * blower.force; | |
} | |
// Fill force grid | |
for(i = (force_grid_sizey - 1); i >= 0; i--) | |
{ | |
var gridy = (i + force_grid_offsety) % force_grid_sizey; | |
var gridposy = grid_basey + (i * cellsizey); | |
for(j = 0; j < force_grid_sizex; j++) | |
{ | |
var gridx = (j + force_grid_offsetx) % force_grid_sizex; | |
var gridposx = grid_basex + (j * cellsizex); | |
var vecx = param_wind_vector_x; | |
var vecy = param_wind_vector_y; | |
for(k = 0; k < num_blowers; k++) | |
{ | |
var blower = blowers[k]; | |
var diffx = blower.posx - gridposx; | |
var diffy = blower.posy - gridposy; | |
var sqdist = (diffx * diffx) + (diffy * diffy); | |
var blowersize = blower.size; | |
if (sqdist < (blowersize * blowersize)) | |
{ | |
var dist = sqrt(sqdist); | |
var weight = 1.0 - (dist / blowersize); | |
vecx += blower.vecx * weight; | |
vecy += blower.vecy * weight; | |
} | |
} | |
var index = ((gridy * force_grid_sizex) + gridx) * 2; | |
force_grid[index] = vecx; | |
force_grid[index + 1] = vecy; | |
} | |
} | |
trail_min_segment_length_sq = param_trail_min_segment_length * param_trail_min_segment_length; | |
sprwidth = sprite_get_width(param_sprite); | |
sprheight = sprite_get_height(param_sprite); | |
var max_mass = max(param_particle_mass_min, param_particle_mass_max); | |
if (max_mass != 0.0) | |
{ | |
particle_scale_compensation = 1.0 / max_mass; | |
} | |
else | |
{ | |
particle_scale_compensation = 1.0; | |
} | |
var sprsize = max(sprwidth, sprheight) * max(param_particle_start_sprite_scale, param_particle_end_sprite_scale); | |
var border_prop = max(0.001, param_particle_spawn_border_prop); | |
var spawnxmargin = (maxx - minx) * border_prop; | |
var spawnymargin = (maxy - miny) * border_prop; | |
// Work out off-screen bounds | |
var os_spawnboundsx = [ (minx - spawnxmargin) - sprsize, minx - sprsize, maxx + sprsize, maxx + spawnxmargin + sprsize]; | |
var os_spawnboundsy = [ (miny - spawnymargin) - sprsize, miny - sprsize, maxy + sprsize, maxy + spawnymargin + sprsize]; | |
var os_spawnxmin = [os_spawnboundsx[0], os_spawnboundsx[1], os_spawnboundsx[2], os_spawnboundsx[0], os_spawnboundsx[2], os_spawnboundsx[0], os_spawnboundsx[1], os_spawnboundsx[2]]; | |
var os_spawnxmax = [os_spawnboundsx[1], os_spawnboundsx[2], os_spawnboundsx[3], os_spawnboundsx[1], os_spawnboundsx[3], os_spawnboundsx[1], os_spawnboundsx[2], os_spawnboundsx[3]]; | |
var os_spawnymin = [os_spawnboundsy[0], os_spawnboundsy[0], os_spawnboundsy[0], os_spawnboundsy[1], os_spawnboundsy[1], os_spawnboundsy[2], os_spawnboundsy[2], os_spawnboundsy[2]]; | |
var os_spawnymax = [os_spawnboundsy[1], os_spawnboundsy[1], os_spawnboundsy[1], os_spawnboundsy[2], os_spawnboundsy[2], os_spawnboundsy[3], os_spawnboundsy[3], os_spawnboundsy[3]]; | |
var os_spawnweights = []; | |
var os_weighttotal = 0.0; | |
for(i = 0; i < 7; i++) | |
{ | |
os_spawnweights[i] = (os_spawnxmax[i] - os_spawnxmin[i]) * (os_spawnymax[i] - os_spawnymin[i]); | |
os_weighttotal += os_spawnweights[i]; | |
} | |
for(i = 0; i < 7; i++) | |
{ | |
os_spawnweights[i] /= os_weighttotal; | |
} | |
do | |
{ | |
update_trails(); | |
var spawnxmin; | |
var spawnxmax; | |
var spawnymin; | |
var spawnymax; | |
var spawnweights; | |
var spawn_all_now; | |
var spawn_in_one_zone; | |
if ((frame == 0) && (param_particle_spawn_all_at_start != 0)) | |
{ | |
// Spawn particles on-screen | |
spawnxmin = (minx - spawnxmargin) - sprsize; | |
spawnxmax = maxx + spawnxmargin + sprsize; | |
spawnymin = (miny - spawnymargin) - sprsize; | |
spawnymax = maxy + spawnymargin + sprsize; | |
spawnweights = 0; | |
spawn_all_now = true; | |
spawn_in_one_zone = true; | |
} | |
else | |
{ | |
// Spawn particles off-screen | |
spawnxmin = os_spawnxmin; | |
spawnxmax = os_spawnxmax; | |
spawnymin = os_spawnymin; | |
spawnymax = os_spawnymax; | |
spawnweights = os_spawnweights; | |
spawn_all_now = false; | |
spawn_in_one_zone = false; | |
} | |
var floored_param_num_particles = floor(param_num_particles); | |
if (num_particles < floored_param_num_particles) | |
{ | |
var new_num_particles = floored_param_num_particles; | |
if ((param_particle_spawn_time > 0.0) && (spawn_all_now == false)) | |
{ | |
time_since_last_particle += timedelta; | |
var spawn_time_seconds = param_particle_spawn_time / 1000.0; | |
new_num_particles = min(new_num_particles, num_particles + floor(time_since_last_particle / spawn_time_seconds)); | |
time_since_last_particle = time_since_last_particle % spawn_time_seconds; | |
} | |
if (array_length(particles) < new_num_particles) | |
{ | |
array_resize(particles, new_num_particles); | |
} | |
for(i = num_particles; i < new_num_particles; i++) | |
{ | |
particles[i] = setup_particle(spawnxmin, spawnxmax, spawnymin, spawnymax, spawnweights, centrex, centrey, view_velx, view_vely, gamespeed, spawn_in_one_zone); | |
} | |
num_particles = new_num_particles; | |
} | |
var dragcoeffs = air_density * param_dragcoeff * 0.5; | |
var update_skip = floor(param_particle_update_skip + 1); | |
for(i = 0; i < num_particles;) | |
{ | |
var respawn = false; | |
var particle = particles[i]; | |
if ((particle.spawnframe + particle.lifetime) <= frame) | |
{ | |
respawn = true; | |
} | |
if (!respawn) | |
{ | |
if (((frame + i) % update_skip) != 0) | |
{ | |
particle.lastposx = particle.posx; | |
particle.lastposy = particle.posy; | |
particle.posx += particle.velx * timedelta; | |
particle.posy += particle.vely * timedelta; | |
if (particle.trail != undefined) | |
{ | |
add_trail_segment(particle.trail, particle.posx, particle.posy); | |
} | |
particle.rot += particle.rotspeed * timedelta; | |
i++; | |
continue; | |
} | |
// look up force grid | |
var gridx = (particle.posx - grid_basex) / cellsizex; | |
var gridy = (particle.posy - grid_basey) / cellsizey; | |
var blendx = frac(gridx); | |
var blendy = frac(gridy); | |
gridx = floor(gridx); | |
gridy = floor(gridy); | |
gridx = (gridx + force_grid_offsetx) % force_grid_sizex; | |
gridy = (gridy + force_grid_offsety) % force_grid_sizey; | |
if (gridx < 0) | |
gridx = force_grid_sizex + gridx; | |
if (gridy < 0) | |
gridy = force_grid_sizey + gridy; | |
var force1x, force1y, force2x, force2y, force3x, force3y, force4x, force4y; | |
var gridtempx1 = gridx; | |
var gridtempx2 = ((gridx + 1) % force_grid_sizex); | |
var gridtempy1 = gridy * force_grid_sizex; | |
var gridtempy2 = ((gridy + 1) % force_grid_sizey) * force_grid_sizex; | |
var index1 = (gridtempy1 + gridtempx1) * 2; | |
var index2 = (gridtempy1 + gridtempx2) * 2; | |
var index3 = (gridtempy2 + gridtempx1) * 2; | |
var index4 = (gridtempy2 + gridtempx2) * 2; | |
var gridforcex; | |
var gridforcey; | |
// Sample grid | |
force1x = force_grid[index1]; | |
force1y = force_grid[index1 + 1]; | |
force2x = force_grid[index2]; | |
force2y = force_grid[index2 + 1]; | |
force3x = force_grid[index3]; | |
force3y = force_grid[index3 + 1]; | |
force4x = force_grid[index4]; | |
force4y = force_grid[index4 + 1]; | |
// Bilinear filter | |
// (could precalculate force differences between adjacent cells to avoid having to do subtractions here) | |
var topval = ((force2x - force1x) * blendx) + force1x; | |
var bottomval = ((force4x - force3x) * blendx) + force3x; | |
gridforcex = ((bottomval - topval) * blendy) + topval; | |
topval = ((force2y - force1y) * blendx) + force1y; | |
bottomval = ((force4y - force3y) * blendx) + force3y; | |
gridforcey = ((bottomval - topval) * blendy) + topval; | |
var thisparticlemass = particle.mass; | |
var particle_velx = particle.velx; | |
var particle_vely = particle.vely; | |
particle_velx += (gridforcex / thisparticlemass) * timedelta; | |
particle_vely += (gridforcey / thisparticlemass) * timedelta; | |
// Apply gravity | |
particle_vely += param_grav_accel * timedelta; | |
// Apply drag | |
var forcex = dragcoeffs * particle.mass * (particle_velx * particle_velx); | |
var forcey = dragcoeffs * particle.mass * (particle_vely * particle_vely); | |
if (particle_velx > 0) | |
forcex = -forcex; | |
if (particle_vely > 0) | |
forcey = -forcey; | |
particle_velx += ((forcex / thisparticlemass) * timedelta); | |
particle_vely += ((forcey / thisparticlemass) * timedelta); | |
particle.velx = particle_velx; | |
particle.vely = particle_vely; | |
particle.lastposx = particle.posx; | |
particle.lastposy = particle.posy; | |
particle.posx += particle_velx * timedelta; | |
particle.posy += particle_vely * timedelta; | |
var view_rel_velx = particle_velx - view_velx; | |
var view_rel_vely = particle_vely - view_vely; | |
if (((view_rel_velx < 0) && ((particle.posx + sprsize) < minx)) || | |
((view_rel_velx > 0) && ((particle.posx - sprsize) > maxx)) || | |
((view_rel_vely < 0) && ((particle.posy + sprsize) < miny)) || | |
((view_rel_vely > 0) && ((particle.posy - sprsize) > maxy))) | |
{ | |
respawn = true; | |
} | |
} | |
if (respawn && (num_particles > floored_param_num_particles)) | |
{ | |
// remove particle | |
particles[i] = particles[num_particles - 1]; | |
num_particles--; | |
} | |
else if (respawn) | |
{ | |
particles[i] = setup_particle(spawnxmin, spawnxmax, spawnymin, spawnymax, spawnweights, centrex, centrey, view_velx, view_vely, gamespeed, spawn_in_one_zone); | |
i++; | |
} | |
else | |
{ | |
if (particle.trail != undefined) | |
{ | |
add_trail_segment(particle.trail, particle.posx, particle.posy); | |
} | |
particle.rot += particle.rotspeed * timedelta; | |
i++; | |
} | |
} | |
frame++; | |
} until (frame > param_warmup_frames); | |
} | |
room_start = function() | |
{ | |
} | |
room_end = function() | |
{ | |
reset(); | |
} | |
layer_begin = function() | |
{ | |
} | |
layer_end = function() | |
{ | |
if ((event_type != ev_draw) || (event_number != 0)) | |
return; // wrong event | |
gpu_push_state(); | |
var oldcol = draw_get_colour(); | |
var oldalpha = draw_get_alpha(); | |
draw_trails(); | |
draw_particles(); | |
if (param_debug_grid > 0) | |
{ | |
draw_set_colour(c_white); | |
draw_set_alpha(debug_grid_alpha); | |
var cellsizex = (views_maxx - views_minx) / (force_grid_sizex - (grid_margin * 2)); | |
var cellsizey = (views_maxy - views_miny) / (force_grid_sizey - (grid_margin * 2)); | |
var grid_basex = force_grid_centrex - ((force_grid_sizex * cellsizex) * 0.5); | |
var grid_basey = force_grid_centrey - ((force_grid_sizey * cellsizey) * 0.5); | |
var i, j; | |
for(i = 0; i < force_grid_sizey; i++) | |
{ | |
for(j = 0; j < force_grid_sizex; j++) | |
{ | |
var gridx = (j + force_grid_offsetx) % force_grid_sizex; | |
var gridy = (i + force_grid_offsety) % force_grid_sizey; | |
var index = ((gridy * force_grid_sizex) + gridx) * 2; | |
var forcevecx = force_grid[index]; | |
var forcevecy = force_grid[index + 1]; | |
forcevecx *= debug_force_scale; | |
forcevecy *= debug_force_scale; | |
var startx = (grid_basex + (cellsizex * (j + 0.5))) - (forcevecx * 0.5); | |
var starty = (grid_basey + (cellsizey * (i + 0.5))) - (forcevecy * 0.5); | |
draw_arrow(startx, starty, startx + forcevecx, starty + forcevecy, 10); | |
} | |
} | |
if (param_debug_grid > 1) | |
{ | |
draw_set_colour(c_yellow); | |
for(i = 0; i < num_blowers; i++) | |
{ | |
draw_circle(blowers[i].posx, blowers[i].posy, blowers[i].size, true); | |
var halfforcevecx = blowers[i].vecx * debug_force_scale * 0.5; | |
var halfforcevecy = blowers[i].vecy * debug_force_scale * 0.5; | |
draw_arrow(blowers[i].posx - halfforcevecx, blowers[i].posy - halfforcevecy, blowers[i].posx + halfforcevecx, blowers[i].posy + halfforcevecy, 10); | |
} | |
} | |
} | |
draw_set_colour(oldcol); | |
draw_set_alpha(oldalpha); | |
gpu_pop_state(); | |
} | |
reset(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment