ID:2587123
 
(See the best response by Kaiochao.)
Problem description:
Hi, guys. I need some help.
I'm trying to make a generic projectile throw. But it on the step proc, it seems to ignore the last parameter. The object moves on a step_size scale and not by the pixel.
This is the proc:
verb
Throw_Magic_Straight()
set category = "Combat"
var/obj/skill/projectile/generic_projectile/bullet = new()
bullet.dir = src.dir
var/life_time = 10
bullet.loc = src.loc
bullet.dir = src.dir
while(life_time>=0)
sleep(1)
if(!step(bullet, bullet.dir, 1))//3rd parameter not working properly
del(bullet)
return 1 //hit
life_time--
del(bullet)
return 0 //didnt hit




I tried to set the object step_size to 1 on this moment. It works for the projectile, but I don't know who the heck it breaks the Move() from all mobs. It changes from GLIDING to JUMPING. It doesn't change the Mob stepsize, only its animation. I don't get this interaction.


My last resort is to make manually a proc that moves the object pixel by pixel and checking colision through pixel_x, pixel_y and bound manipulation. I didn't want to do this. I couldn't make it work with any of the movement procs, they ignore the pixel parameter.


Here is the video from the Verb without breaking the animation, ignoring the "step()" last parameter:
https://youtu.be/Dusd3uI33TU

Any thoughts?




Best response
Pixel movement is a project-wide movement mode, separate from tile movement mode. Tile movement gets tile gliding, pixel movement doesn't, and they don't mix.

Your options are to recreate tile gliding for things that still move by tiles, or recreate pixel movement for projectiles (or go full pixel movement for everything).
In response to Kaiochao
So, it is possible to get pixel movement only for objects, not mobs? I wasn't very clear to me.
If so, there is an option to set movement type for an instance or type of object?

In response to Keryar
By project-wide, I meant that you can't. There's no difference between objs and mobs here either.
In response to Kaiochao
I've found some solutions that, by hand, makes an object move pixel by pixel towards a target. It was my idea as last resort.
Is this a bad solution? If yes, why?

I'm trying not to redo the tile gliding by hand only for the pixel projectile.
In response to Keryar
Keryar wrote:
I've found some solutions that, by hand, makes an object move pixel by pixel towards a target. It was my idea as last resort.
Is this a bad solution? If yes, why?

Not necessarily. It's easy to make the object move by just setting step_x and step_y. The catch is that you have to do your own collision detection. That can be a good thing but generally only if you are doing less work than the built in system. For example, setting step_x and step_y means that the loc never changes and things like Enter() and Exit() and Bump() are never called. So if you want a projectile that just flies until it finds a dense bounding box, it might be a good idea to bypass the default movement. I'd suggest using the profiler to compare methods if you're not sure which one is more efficient.

But, it might be easier to just use animate() to emulate gliding?

In the case of moving by hand (step_x or pixel_x++), i was planing on making the collision by hand already. I believe I have enought information to start doing this. Thank you.

I don't know how the animate would help on this case.
In response to Magicsofa
Assuming OP doesn't want to enable pixel movement mode in their project, step_x/y can't be used at all, so it would be pixel_x/y or transform instead. (If you're going to use step_x/y, you should use built-in pixel movement)

You can still use built-in collision detection in custom pixel movement. It just depends on how you apply the conversion of a pixel mover's custom pixel coordinates into its new loc and visual pixel offsets.
// set animate_movement = NO_STEPS to disable tile gliding on pixel movers

// convert pixels to whole tiles
var/whole_tile_x = ceil(position_x / TileWidth)
var/whole_tile_y = ceil(position_y / TileHeight)

// visually offset to the precise pixel position
pixel_x = position_x - 1 - (whole_tile_x - 1) * TileWidth
pixel_y = position_y - 1 - (whole_tile_y - 1) * TileHeight

// move to the tile at that pixel position, leaving z unchanged
// this would call Bump() if a collision would normally occur
Move(locate(whole_tile_x, whole_tile_y, z))

(position_x, position_y) are the custom pixel coordinates of the projectile. This code would be run whenever those values are changed, to update the BYOND engine on the projectile's position.
ceil(x) is -round(-x), the ceiling function, to round up.
TileWidth and TileHeight should be constants that match world.icon_size.
e.g. When TileWidth is 32,
If position_x = 1, then x = 1 and pixel_x = 0 (left edge is at left edge of the world).
If position_x = 33, then x = 2 and pixel_x = 0 (left edge is right of the left-most tile).


Smooth motion is achieved by rapid tiny steps. There's a lot I could say (and have said) about this subject, but the bottom line is that you want a high framerate if you want smooth motion. Framerate is controlled (and unfortunately limited by) world.fps and client.fps, which have always defaulted to an awful 10 FPS.

In BYOND, time is always in units of 1/10th seconds (aka deciseconds). A tick always has length of world.tick_lag. If you sleep by 1 in your loop, even if you're using a higher world.fps, you're limiting yourself again to 10 steps per second. To sleep by a single tick, you should sleep by world.tick_lag. I'll put it in code tags for emphasis:
sleep(world.tick_lag) // good
sleep(1) // bad
In response to Keryar
Keryar wrote:
I don't know how the animate would help on this case.

Whenever something moves by a whole tile, you could animate the slide yourself. So if something moved West, you could set the pixel_x to -32 and then call animate(src, pixel_x = 0, ...)