Get arrows and spears to arc and perspectivize properly #46

nwinter opened this Issue Jan 2, 2014 · 9 comments


None yet

2 participants

nwinter commented Jan 2, 2014

Currently when an Archer or Arrow Tower shoots an Arrow, the Arrow does properly rotate in the x-y plane so that it's facing its target, but it doesn't handle the other ones (yaw? pitch? roll?). So it doesn't point up when its z-velocity is positive, nor down when it's negative. It also doesn't appear shorter when flying more parallel to the camera's line of sight than it does when flying orthogonally.

screenshot 2014-01-02 12 11 52
screenshot 2014-01-02 12 13 06

I think one would just need to do some math to update the Arrow Thang's CocoSprite imageObject transform properties or their shorthands (scaleX, skewY, rotation, etc.) according to the velocity of the Arrow and the Camera's view. I was trying to do it but got confused. If you like affine transforms or computer graphics, you might have some fun with this one!

It's easy to test: just play Zone of Danger and get the arrows looking good. You might want to make the arrows travel a lot slower by editing their maxSpeed in their movement.Moves Component, which would emphasize their arcs. One of the reasons arrows travel so fast now is that they don't look good when actually needing to arc.

The code to write may be in or related to app/lib/surface/ in the updateScale and updateRotation methods.

@ItsLastDay ItsLastDay added a commit to ItsLastDay/codecombat that referenced this issue Mar 17, 2014
@ItsLastDay ItsLastDay added perspectivizion for arrows (issue #46) 97f9e54

Hey there! I've been looking into this issue for like 5 hours in a row today.

  1. Currently, when you switch Arrow's maxSpeed, it does not travel significantly slower. Instead, it just truncates the maximum attack range and that's it.
  2. I don't understand why it happens, but arrow tower disrespects it's max range:
  3. I've reimplemented ( combat.Arrow.launch so it shoots at 45 degree angle when possible, that actually looks good - arrows emanating from arrow tower travel slow, try it yourself. The 2) is still there, however. The major downside here is that the locomotive force of an arrow is significantly lower, so it doesn't hit ogres much. Also the lack of speed now gives a window to dodge the bullet. Overall: you can't complete Zone of Danger with this!

So you can't just migrate to different arrow speed that easily. At least, when affecting all levels. Maybe do this in some want-to-be-looking-good ones?
I'll work out changes to updateRotation and updateScale methods in CocoSprite in order to arc properly, then test it against my implementation of combat.Arrow.launch, to ensure it works nice when arrows actually do move in large and visible arcs.

PS by the way, on the screenshot, I did not declare enemy, but it still works and shows victory. That's because I'm always attacking enemies[0] actually. This is a poor solution and must not pass.

nwinter commented Mar 23, 2014

Wow, thanks for figuring all that out. I've pulled in your changes and now added a "maximizesArc" config property to the Arrow, so you can toggle the old behavior vs. the new behavior. Check it out in the Arrow code in the live site.

I liked how the arrows were arcing, but I realized that it had the same problem that everything else which arcs has: it looks too floaty. I think this is because we're using realistic world gravity of 9.82 m/s^2, but everything in the game is kind of super-sized. I tried setting Zone of Danger's gravity to 30 m/s^2 and the arrow arcs and jumping looked much cooler. I might try this on some more levels, too. If you grab the latest DB, you can try toggling the maximizesArc on in the Arrow config to see how it looks.

I think that the change to gravity also made it so that attacking enemies[0] fails once more–at least, when I just tried it, it loses, whereas actually hitting the nearest one does succeed.

@ItsLastDay ItsLastDay added a commit to ItsLastDay/codecombat that referenced this issue Mar 23, 2014
@ItsLastDay ItsLastDay done rotating, looks good in Zone of Danger (#46) 446726a
@ItsLastDay ItsLastDay added a commit to ItsLastDay/codecombat that referenced this issue Mar 23, 2014
@ItsLastDay ItsLastDay Now absolutely done scaling (#46)
also commented math's out.

Glad you liked!
After some more hours of trial-end-error, I've come up with a solution for arrows so they actually point along the arc, check this out, for example:
This is highly ad-hoc, because it works only with my combat.Arrow.launch and there are empirical constants involved. Tell me what we can do with that. I've pull requested in case you need my code, because it's 00:30 here in Russia and I'm going to sleep soon.

By the way, how can I update my DB when I already have one? Mongorestore errors that it can't add data cause of unique index, because it (obviously) interferes with existing data. I suppose there's another command to do that?

nwinter commented Mar 23, 2014

I like this for Dungeon Arena, and it works when we are using maximizesArc (which I sadly don't think we'll be able to use in existing levels without a lot of rebalancing, but can use going forward), but something is up when they're flying along a straighter path:

screenshot 2014-03-23 13 57 07

I'm merging it in, but only when maximizesArc is true for now.

We don't yet have a way to selectively merge in part of the new dump. If you need stuff from your current DB and also from the new DB, then it has to be a manual process, either of bringing forward your old things after restoring the new dump, or of manually grabbing the new part of the new DB.


Great! I hope that artisans will take this feature into account.
One can always change visual effects to fit the desired result by managing constants in updateRotation. For example, if you need less curving on a straight path, you change
Math.max(factor / 90, 0.4)
to something like
Math.max(factor / 90, 0.2).
In case of any questions, feel free to ask me! (it is addressed to anyone who wishes to make this more beatiful)

nwinter commented Mar 24, 2014

I guess the thing that is needed is to not just infer the arrow's position along its flight path by its z-velocity, because that only works if the arrow is shooting from a position with equal z to its target. It should take into account the difference between the start and end z-values.


"that only works if the arrow is shooting from a position with equal z to its target" - not correct.
Notice that in calculateArcVelocity we take the difference of pos.z and end.z: the denominator under Math.sqrt is distance - (targetPos.z - pos.z). Consider the following picture:
Denote d = targetPos.z - pos.z

  1. the upper case is trivial: d = 0. Here we have roughly the same angle as when we shooted out, because the elevation time equals the descendence time (and, therefore, velocity.z is the same at endpoints).
  2. the lower-left case: d < 0 (target lower than shooter). In this case, when we subtract d from distance, we get higher value of denominator. Which leads us to initial velocity.z smaller than in case 1. So when we calculate the curve, it is smaller, which is natural, because we want to shoot more horizontally. Moreover, the descendence time is much greater than the ascendance - so velocity.z is highly negative, and more at absolute value than initial. So we get very steep slope, which is natural too (look at the picture - it's very close to 90 degrees).
  3. the lower-right case: d > 0. Here we have denominator lower than in case 1, so the initial speed is higher => higher velocity.z stands for higher slope. And the descendence time is so small (the arrow must 'climb' with it's high initial speed to some height, then descend slightly), that at the end arrow is barely curved. Which is, again, natural.

Don't get me wrong: the code in combat.Arrow stands for 45 degree angle. But the hack I did on calculating arrow curve by looking at it's velocity.z is actually working independent of that (it's just an effect we see on the surface), with the help of gravity. Math and common sence prove it to be right.

@nwinter nwinter added a commit that closed this issue Mar 28, 2014
@nwinter nwinter Fixed #46. cf81d51
@nwinter nwinter closed this in cf81d51 Mar 28, 2014
nwinter commented Mar 28, 2014

Thanks for the diagram and for explaining it more. I played around with it and came up with something that appears to work almost perfectly in all the different cases I tried:

      vz = @thang.velocity.z
      if vz and speed = @thang.velocity.magnitude(true)
        vx = @thang.velocity.x
        heading = @thang.velocity.heading()
        xFactor = Math.cos heading
        zFactor = vz / Math.sqrt(vz * vz + vx * vx)
        rotation -= xFactor * zFactor * 45

Now I don't know why the 45 isn't 90, which is what I thought it should be, but I'm always rusty at these things.


Pleasant to see that you could match your desires with my solution!
Concerning 45 instead of 90:
my original reasoning was that if you fire perfectly to the left, with the target having same z-coordinate as shooter, you should see the 45 degree arc at the start and -45 at the end (because combat.Arrow.launch was written with 45 in mind). That should happen when 'xFactor' (which handles position to camera) is fixed at 1 and 'zFactor' is 1 (or very close) at the start and -1 (or close value, same as at start) at the end point. But when z-coordinates are not equal, zFactor is not the same at endpoints, for example if end speed exceeds start speed, zFactor can come above 1 and that will result in > 45 degree arc.
These reasoning works if zFactor equals to velocity.z.current / 'some common value of velocity.z.start', which I was trying to simulate with velocity.z.current / 12.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment