ID:2110793
 
(See the best response by Kaiochao.)
Howdy so I'm working on pixel projectiles and I'm up to the point where the animation is working just fine but I'm not entirely sure how to detect collisions because animation won't actually let me move the projectile to bump with anything.

Any ideas? Thank you :)

Code:
Projectile
parent_type = /obj
icon = 'Projectile.dmi'

var
speed = 0.2 // Time to move 32 pixels
atom/owner

Fireball
icon_state = "Fireball"

New(var/turf/location, var/Vector/path, var/atom/_owner)
..()
owner = _owner
Move(location)
animate(src, pixel_x = path.x, pixel_y = path.y, time = speed * (path.Magnitude() / 32))

Best response
You want to use animate() to achieve the appearance of perfectly smooth movement, but you still want collision detection along the projectile's visual movement path?

One way to accomplish that is to create a separate, invisible, dense object to handle collisions. You would physically move this object over time, copying how the animate() interpolates the visual position.

It might be better to use /Projectile for the "physical" object, and some other type of object (such as /obj) for the purely visual effect, because /Projectile would be a good place to handle those interactions, since the purely visual effect doesn't really do anything. The visual effect should react to changes in the physical object, e.g. destroy the visual effect when the physical object is also destroyed.

Projectile
density = TRUE

var obj/visual
var lifetime_ticks

// etc.

New(turf/location, Vector/path, atom/_owner)
..()
owner = _owner

// create the visual object
visual = new (loc)
visual.step_x = step_x
visual.step_y = step_y
visual.appearance = appearance

// make the physical object invisible
invisible = TRUE

var lifetime = path.Magnitude() * speed / 32
animate(visual, pixel_x = path.x, pixel_y = path.y, time = lifetime)

// how many ticks until the animation is done
lifetime_ticks = lifetime / world.tick_lag

// start update loop

Del()
visual.loc = null
..()

Bump(atom/Obstacle)
// etc.

proc/Update() // this is called every world-tick
if(lifetime_ticks-- <= 0)
// the animation has ended by now, so clean up
loc = null
// stop update loop

else
// move the projectile to calculated position


Or, you could do your own collision detection. I recommend against this if your projectiles can move very fast, i.e. significantly farther than their size per world-tick, because BYOND does movement subdivision for you to avoid skipping over potential collisions. But if you really want to, you could use bounds(x, y, width, height, z) to check for obstacles in a position that you calculate using the same position interpolation used by your animate().

Off-topic, but I thought I should comment on a couple things:

Calling Move(location) in New() won't do anything since objects are initialized with their loc equal to the first argument passed to new type (args).

Traditionally, speed refers to an amount of distance covered per unit of time (e.g. meters per second, pixels per second, or pixels per world-tick). Speed should be something that makes objects move faster when the number is higher, but yours does the complete opposite of that, so I wouldn't use the word "speed" for that. Sure, it's related to speed (inversely), but it's not the speed itself. Maybe inverseSpeed?
Well one way I found which works is this. I just don't use animate.

    New(var/turf/location, var/Vector/path, var/atom/_owner)
..()
owner = _owner
Move(location)
var
magnitude = path.Magnitude()
Vector/trajectory = path.Normalize()
xshift = 0
yshift = 0
originalX = x * 32
originalY = y * 32
turf/support

for(var/i = 0, i < magnitude, i++)
xshift += trajectory.x
yshift += trajectory.y

if(i % speed == 0) sleep(0.167)

pixel_x = round(xshift)
pixel_y = round(yshift)

support = locate(round((originalX + pixel_x + 16) / 32), round((originalY + pixel_y + 16) / 32), 1)

if(support.density) del src
for(var/atom/A in support) if(A.density && !istype(A,/Player)) del src
In response to Zecronious
This is soft-coded pixel movement with custom tile-based collision detection, and it doesn't use an update loop when it probably should. The movement can appear choppy over a network, unlike the animate() approach. The collision detection subdivides movements to 1 pixel-width increments.

Sure, it works.
Wait...Since when is Update() a thing? BYOND calls that automatically every tick? Because I don't see it called manually there.
In response to Tens of DU
It's not a built-in proc. I meant that it should be called every tick, not that it already is, because it's a pretty common programming pattern that every game developer (at least, if you're not using BYOND) should be aware of.