ID:1879615
 
Okay, so, I wanted to make beams work in my own way, inspired from a thread I saw the other day about beam attacks. I managed to get it working properly, so long as you're using a relatively accurate set of values.

It's a bit complicated, but I'll give my best shot to describe what I've done...

In my game I'm using mouse controls to allow the player to aim attacks via a given angle based on the relation from the center of the screen to the mouse position.

For example, if I have a mob at position 1,1 and my mouse is at 5,5 then the aiming angle is 45 degrees. This is how pretty much how the player points their attacks, of course, the system I'm using is much more accurate, taking the position based in pixels, rather than tiles.

So, how do we get a beam to work? Well, visually, I did what I suggested in another thread: I used a simple small icon, then I applied color to it, then scaled it based on how far I wanted the beam to go. I also had to translate it by half the scaled distance, since scales happen from the center out. Finally I angled it to match the angle of attack. I displayed it via an image and all is well visually.

Now the tough part: How to make it hit a target?

Let's say we have two mobs, blue as the source, and red as the enemy. The target angle is represented by green.



Now, I could just try to match the angle to the target angle, and if it's the same, it's a hit, but because of how accurate the game is, that doesn't exactly help, and would require the player to be incredibly accurate.

I could also check to see if the target is within a certain range of an angle from the target angle. Let's say 5 degrees within the target angle.



The problem with this solution is that it makes a cone, causing targets closer to miss while further targets are hit more easily. It would be much easier to code and use, but I want accuracy!

What I want to do, is something like this:



How do we do this? Well, it all comes down to one question: Is the target between the dotted lines?

To find this out, I found a helpful procedure online, which uses some relatively simple math to find out of a point is to the left or right of a line.

IsLeft(ax, ay, bx, by, cx, cy)
var/value=(bx - ax)*(cy - ay) - (by - ay)*(cx - ax)
if(value>0) return 1
else return 0


Now, with the help of this procedure, I just need to get two lines, and check to see if the position of the target is to the right of one line, and to the left of the other. If this is the case, then the target is within both lines, and within the beam.



In the example, I've plotted out two points as the origins of our new lines. The way I get these points is by taking the target angle, modifying it by a right angle, and then adding along the new angle that's created to get at an offset position. Here's an example of what that looks like (assuming variables have been initialized already):

mod_x=10
mod_y=0
l_off_x1=position_x+cos(angle-90)*mod_x
l_off_y1=position_y-sin(angle-90)*mod_y


Since we didn't need to adjust the y position relative to the angle give, we only modify the x value along the new angle. The modified x value tells us how far the edge of the beam is away from the center. As a result, this allows us to control the width of the beam, the larger the x offset, the larger the beam.

We now have a position defined as left_offset_x,left_offset_y. This exist as a spot that is left of the angle given. This position is denoted in yellow.



Now, we do the same modification we did to the original line, and apply it to the new position, using the original angle, to give us a line that is parallel to the original angle when we plug the variables into the IsLeft() proc.

l_off_x2=l_off_x1+cos(target_angle)*2
l_off_y2=l_off_y1-sin(target_angle)*2


Here I used only a small adjustment of 2, as the angle will be the same regardless of the position of the second point on the line.

Now, we plug everything into IsLeft(). The target.cy and cy variables are obtained by getting a list of mobs within range of the beam, then checking their precise position to see whether or not they'll be hit.

IsLeft(l_off_x1, l_off_y1, l_off_x2, l_off_y2, target.cx, target.cy)


If this returns true (1), this means that the target is to the left of the left edge of the beam, and should not be hit. If it returns false (0), then it is to the right of the line, and has potential to be within the beam.

We then perform the same check with another set of points, based on the right side of the beam. For this we use target_angle+90 instead of -90, to show that it's to the right of the original position. The offset for x should be kept the same to make sure that the beam is not lopsided.

One everything is said and done, we should be checking to make sure the target is both to the right of the left edge, and to the left of the right edge, if it is, it is within the beam, and affects such as damage can be applied.

Only one step left. The mathematical beam that we've created stretches across the entire map, technically, and enemies at the exact opposite of where you're aiming will end up getting included in the beam as well.



In order to keep from hitting them, we'll do one final IsLeft() check, using the position of the source(cx,cy), the position of the left offset(l_off_x1,l_off_y1), and finally of the target(target.cx,target.cy).

IsLeft(cx,cy,left_off_x1,left_off_y1,target.cx,target.cy)


This line runs perpendicular to the original angle, and since the beam is traveling outward from the center, it is to the right of the line. So, if the target is to the right of the new line we're checking with IsLeft, then it is also in the path of the beam, while also not being on the exact opposite side of the source


In the end, I know that this code is nowhere near efficient, but it is a way to do it viably, and from my testing, it works pretty well.




Full album of images: http://imgur.com/a/vGMV5


Again, I know it's not the best, but it took me some time to figure this all out and I wanted to share for anyone trying to do something similar in the future. Perhaps we can all help each other find a much more efficient and well designed method.
I went ahead and ported this a week ago or so.

You can see it here

I was intending to make it into a lib but my own fear of judgement prevents me from actually doing so.
Perhaps we should however get something on github and work on improving it together.

I just think it's a cool little thing that can be handy.
Cool post, Bravo.
In response to Rotem12
Rotem12 wrote:
I went ahead and ported this a week ago or so.

You can see it here

I was intending to make it into a lib but my own fear of judgement prevents me from actually doing so.
Perhaps we should however get something on github and work on improving it together.

I just think it's a cool little thing that can be handy.


That looks great! Unfortunately I wasn't able to get the demo you included working properly (it just kind of crashed after a few seconds, maybe I have the wrong byond version?)

A library would be fantastic!

In response to Bravo1
I'm using latest stable, what version are you using?

EDIT: It doesn't seem to crash for me with latest beta as well.
Ah, I was in 1287, works fine now. Looks good as well. Again, would love to see a library from it as I wanted to include electrical type weapons in the project I'm working on.
In response to Bravo1
Bravo1 wrote:
Ah, I was in 1287, works fine now. Looks good as well. Again, would love to see a library from it as I wanted to include electrical type weapons in the project I'm working on.

I went ahead and released one, it's also on GitHub, I don't know if I've done things right or efficient and I'm quite insecure about it, my first ever released library.

I did this using a method of rotated square collision by basically creating a rectangle that represented the area covered by the beam and doing vector collision based on that. This seems like a much more efficient method, however and will have to try it out sometime.

Btw, those two points on the side of the circle are called tangents. ;) Just so you have something more technical to call them than "line width origins" or whatever. Lol.
Those words made my brain hurt so I'm just going to smile and nod.

Seriously though, that sounds interesting, would you happen to still have the code for it? I'd love to compare the process time between methods, possibly even come up with a hybrid of the two, if possible?
In response to Rotem12
Rotem12 wrote:
Bravo1 wrote:
Ah, I was in 1287, works fine now. Looks good as well. Again, would love to see a library from it as I wanted to include electrical type weapons in the project I'm working on.

I went ahead and released one, it's also on GitHub, I don't know if I've done things right or efficient and I'm quite insecure about it, my first ever released library.

Awesome... but for some reason I can't seem to download it D:

I'm just tripping over myself when it comes to your work, very sorry! o__o
In response to Bravo1
I did but I wiped my computer the other day and that was one of the things I didn't think about backing up. I still have the pseudocode for it. I can probably whip it up again for testing.
In response to Bravo1
Bravo1 wrote:
Awesome... but for some reason I can't seem to download it D:

It's my first time releasing a lib so I wouldn't be too shocked if I did something wrong but I can download it just fine so I'm not exactly sure what's going on.

There's also a link to GitHub where you can click "Download Zip", it was important to me to include it there in case someone had better ideas or wanted to expand it.




In response to Kats
Kats wrote:
I did but I wiped my computer the other day and that was one of the things I didn't think about backing up. I still have the pseudocode for it. I can probably whip it up again for testing.

Cool!

@Rotem12: turns out it was my client, it's working now but I've already got some feedback.

So far the only issues I've had is with the 32 factor, I have a world that's 64 icon size so it didn't work at first. I went and swapped all the 32 references with world.icon_size and it started functioning... somewhat properly.

I think I'm getting the destination points wrong though. Are those supposed to be a position in pixels, or is that tiles?

In response to Bravo1
Bravo1 wrote:
I think I'm getting the destination points wrong though. Are those supposed to be a position in pixels, or is that tiles?

Position in pixels, that's why I'm multiplying x and y by 32. It should work with pixel offsets but I can't say I've tested it that way.

Considering the lightning bolt is basically line segments that probably hold pixel offsets I always assumed it works flawlessly, if it was off it would look weird somewhere in the connection but that doesn't happen.

If you have suggestions about how to make it more usable, I'd love improve it, maybe add few simplified procs accepting x, y and pixel offsets?

Pirion had an idea to allow passing targets and have the lightning branch out towards them, I hope I can implement it easily, it sounds like a cool addition.



I updated the library, I tested it with icon size of 64, it looks even better.

It's probably because you get more line segments since the pixel distance doubles. I can create this effect for icon size of 32 as well but I do fear performance a little bit, the more line segments the better it would look but I'm a bit paranoid.

I should note for icon size of 64 I used a different icon segment.
https://dl.dropboxusercontent.com/u/51176364/segment64.dmi

Segment icons should be 1 pixel width but the height (thickness) can be changed as you see fit.
I learned something useful today. Great post, Bravo.