ID:151738
 
I was messing around with some ideas the other day and I decided to work on some visual effects. If you played Castlevania, you notice that when you get hit or die, particles of blood spray from you randomly other than using a pre-made sprite. It gives it a more realistic feel and makes it look neater, in my opinion.

What I ended up doing to replicate that effect was spawning multiple instances of the same object and changing the pixel_x and pixel_y randomly of each object. I hard-programmed that 10 different objects spawn when the proc is called.

My question to you is: Would this be a good method to achieve this, or is there some sort of issue that should be taken into consideration when creating that many objects at the same time? Also, is there a method to spawn multiple objects at the same time without having to hard-code them?
I'd just use the missile() proc with some random turfs as targets. (At least, that's what I do to make puffs of smoke pop out of traveling robots in Gears & Peers.) I wouldn't bother with actual atoms for a purely visual effect. =)
I guess the most desirable method depends on exactly what visual effect you want. I don't think your method would cause problems (unless you have really a LOT of objs and so are in danger of hitting the limit, but that's a non-issue), just make sure you don't create too many objs that it takes too much CPU time. Also, if you can, try to cut down on objs by using other, visual-only methods (what's for sure is that visual-only effects shouldn't be interactable). These include (pixel-offset) overlays and /image objects (I don't think missile() would be very suitable here, but you can give it a try).

ArcaneDragonX wrote:
Also, is there a method to spawn multiple objects at the same time without having to hard-code them?

Uhm, well, there should be... you don't have to hardcode it (by duplicating code for each obj) if you just use loops? I don't know the exact manner of the spawning you want, so I can only give a random example.
proc/Create_Objs(num_of_objs,turf/baseloc)
var/list/objs = new
var/list/target_locs = new
for(var/turf/T in range(baseloc,1))
target_locs += T
while(num_of_objs--)
var/T = pick(target_locs)
objs += new /obj/effect/blood(T)

//you want to alternate the objs' pixel offsets as an effect, right
spawn(1) //spawning here to not delay the caller
for(var/i = 1 to 10) //alternate offsets 10 times
for(var/obj/o in objs)
o.pixel_x += rand(-16,16)
o.pixel_y += rand(-16,16)
sleep(1)
//after we're done, clean up the objs
for(var/obj/o in objs)
del o

EDIT: Forgot the sleep(). xD
In response to ACWraith (#1)
obj/bloodspatter
layer = MOB_LAYER+1
icon = 'blood.dmi'
var
minstep = 4
maxstep = 7
pixelrange = 2

proc/bloodspatter(var/mob/center, var/n = 10, var/dir = 0)
var/obj/bloodspatter/b = new()
var/list/turfs = oview(center.loc, 2)

if(!dir)
for(var/T in turfs)
if(isturf(T))
continue
else
turfs -= T
//handle the directions differently for diagonal and cardinal
//diagonal:
else if(dir&(dir-1))
for(var/T in turfs)
/*
OO
OO /
OOX
OOOOO
OOOOO
*/

var/d = get_dir(center.loc, T) ^ dir
if(isturf(T) && (d&(d-1)))
continue
else
turfs -= T

//cardinal:
else
for(var/T in turfs)
/*
OOO
OOO
OOX-
OOO
OOO
*/

var/d = get_dir(center.loc, T)
if(isturf(T) && (dir & ~d))
continue
else
turfs -= T
for(, n>0, n--)
b.icon_state = pick(icon_states(b.icon))
b.pixel_step_size = rand(b.minstep, b.maxstep)
b.pixel_x = rand(-b.pixelrange, b.pixelrange)
b.pixel_y = rand(-b.pixelrange, b.pixelrange)
missile(b,center.loc,pick(turfs))


Still not sure if I quite like how it turned out. Going to fiddle around with it some more, I think.

One comment is that about a third of the particles get "wasted" by going to an adjacent turf and very quickly disappearing (after only one or two ticks). This reduces the volume of it, but I think it looks better, giving a bit more of an initial impact. You can add - oview(center.loc, 1) to the turfs declaration to compare.

The little diagrams show what the resulting choice of turfs is. Might have preferred the diagonal one to be more triangular but I wanted to stick to bitwise operations to save time.
In response to Garthor (#3)
Ah, missile() IS suitable for this! That looks very nice. =)
In response to Kaioken (#4)
I decided to work on this a bit more. I changed the shapes to be rounded (or else the diagonal missiles last noticeably longer) and made narrow and wide directional spatters. I also added pixel offset arguments so you can, for example, center the spatter on somebody's crotch, head, crotch, arm, crotch, etc.

//actually performs the graphical work of the bloodspatters, for code reuse purposes
proc/do_bloodspatter(var/mob/center, var/list/turfs, var/n = 10, var/px = 0, var/py = 0)
var/obj/bloodspatter/b = new()
for(, n>0, n--)
b.icon_state = pick(icon_states(b.icon))
b.pixel_step_size = rand(b.minstep, b.maxstep)
b.pixel_x = rand(-b.pixelrange, b.pixelrange) + px
b.pixel_y = rand(-b.pixelrange, b.pixelrange) + py
missile(b,center.loc,pick(turfs))

/*
//highlight the turfs, for debugging
for(var/turf/T in turfs)
T.overlays += 'overlay.dmi'
spawn(10)
T.overlays -= 'overlay.dmi'
*/



//graphical effect: sprays n units of blood around center
proc/bloodspatter(var/mob/center, var/n = 10, var/px = 0, var/py = 0)
var/list/turfs = orange(center.loc, "5x3") | orange(center.loc, "3x5")

//filter out non-turfs
for(var/T in turfs)
if(!isturf(T))
turfs -= T

do_bloodspatter(center, turfs, n, px, py)


//graphical effect: sprays n units of blood around center, towards dir, in a wide pattern
/* (NE) (E)
OOO OO
OOOOO OOO
XOO XOO
OO OOO
O OO
*/

proc/bloodspatter_wide(var/mob/center, var/n = 10, var/dir = 0, var/px = 0, var/py = 0)
if(!dir)
bloodspatter(center, n)
return

//formula works as exclusion of direction, so just turn it around
dir = turn(dir, 180)

var/list/turfs = orange(center.loc, "5x3") | orange(center.loc, "3x5")

/* diagonal (NE):
*/

if(dir&(dir-1))
for(var/T in turfs)
var/d = get_dir(center.loc, T) ^ dir
if(isturf(T) && (d&(d-1)))
continue
else
turfs -= T
/* cardinal (E):
*/

else
for(var/T in turfs)
if(isturf(T) && !(dir&get_dir(center.loc,T)))
continue
else
turfs -= T

do_bloodspatter(center, turfs, n, px, py)


//graphical effect: sprays n units of blood around center, towards dir, in a narrow pattern
/* (NE) (E)
OO OO
OOO XOO
XOO OO
*/

proc/bloodspatter_narrow(var/mob/center, var/n = 10, var/dir = 0, var/px = 0, var/py = 0)
if(!dir)
bloodspatter(center, n)
return

var/list/turfs = range(get_step(center.loc, dir), 1)


if(dir&(dir-1))
//remove the center's turf
turfs -= center.loc
//remove the 'top-right' hole
var/turf/t = get_step(center.loc, dir)
if(t != get_step(t, dir))
turfs -= get_step(t, dir)

//remove non-turfs
for(var/T in turfs)
if(isturf(T))
continue
else
turfs -= T

else if(dir)
turfs -= range(get_step(center.loc, turn(dir, 180)), 1)
for(var/T in turfs)
if(isturf(T))
continue
else
turfs -= T

//we may end up with an empy list at the edge of the map
if(!turfs || !turfs.len)
return

do_bloodspatter(center, turfs, n, px, py)