Nepeo's Recent Forum Activity

  • Spotted a Nepeo out in the wild and figured a ping might be appropriate to call attention to this. Would it be possible for you to provide the algorithms you used to generate cellular and voronoi noise? Or at least how you generated the original random distribution of points for the noise, which the rest of the noise can be calculated according to. The goal would be to get the node, vertex, and edge data if possible.

    Mmm so yeah I originally implemented this code, I'm not sure if it's been changed since I left Scirra but I'd guess not as it would change the behaviour.

    At its core it's a fixed tile based Voronoi implementation, this means it doesn't need to generate the whole grid ahead of time and allows the grid to be effectively infinite. But it also means the cells cannot vary in size.

    As such it's relatively easy to get the center of a cell either from the XY of a tile, or an XY location ( slower ). Getting the vertices would be much harder, as the edge information doesn't exist. So it would have to be derived from the centers of the surrounding tiles.

    If you are doing more advanced things... you might want to look into generating the cells yourself using JS. Provided you are working in a finite area it gives a lot more flexibility, and would allow you to do more work ahead of time making queries cheaper to perform.

    I'm happy to chat a bit about how to implement this stuff if you are interested. Also Red blob games talks a bit about how to use Voronoi cells and libraries they use in JS. Poisson disc sampling is a useful subject here as well, as it gives you better distribution/size/variation of cells. Sebastian Lague probably has some videos on both subjects as well, although I forget the exact case he used it for.

  • I forget the exact mechanics on how I normalised the output, but perlin noise normally returns a value between -1 and 1. It doesn't fill out the whole range, due to how the math works. Values aren't linearly distributed along that range. There are also areas where the value is exactly 0. I believe I made some adjustments to try and suppress these artifacts for users, because it's really frustrating when all your results come out as 0 ( trust me, I've been there... ).

    As previously discussed on this topic you can adjust the distribution by trimming a bit off the bottom and a bit off the top, then rescaling the value. Another good technique is increase the value to a power, as this adjust the distribution of the points ( higher power increases the likeliness of highs and lows ).

    By using FBM you are merging several copies of the noise field at different scales, and taking the average value. As such you may be exposing areas of the distribution curve you weren't hitting before, giving you a higher distribution. You could get a similar effect by changing the scale of your input values.

  • rexrainbow

    I assume the CSV plugin is no longer supported? Eren converted it to be used with C3 (which was great btw).

    but I keep coming across a 'bug' in it that I was wondering could/would it ever be fixed?

    Basically the bug is that the CSV.CurRow variable is not protected in scope. So if you loop through the rows of an CSV and inside that loop if you call a function that also loops through the same CSV it blows away the CSV.CurRow for the outer loop.

    There are of course work-arounds but I was just wondering if I should file a bug report or is it even possible to fix/is it even supported?

    I hate that I used this plugin everywhere, but honestly I haven't seen a better way to easily reference a DB or CSV in this case. Being able to use the field names is pretty great. i.e. CSV.At("name",CSV.CurRow) etc..

    Nepeo would you happen to know?

    Yeah I expect the plugin just has a single variable for tracking that information, so if you use it in a nested way the inner one overrides the value. Then depending on how that was implemented it would break the parent call. It needs to use a stack, or similar technique to restore the state after the inner one completes. I remember writing similar protections into the JSON plugin.

    I can't really help a huge amount more here to be honest... I haven't had time to pop into the C3 community for quite some time to be honest ( as people have likely noticed ). I know enough to produce a plugin for this but I wouldn't have time to maintain it.

  • You do not have permission to view this post

  • I'm not sure I quite understand what you were want, but hopefully I can help a little.

    By default the system random expressions use the browsers inbuilt PRNG, which uses a random seed ( the seed is unknown to the consumer, and cannot be set ).

    The AdvancedRandom plugin has its own PRNG, separate to the browsers one. It uses this PRNG as a source of randomness for all its expressions. This PRNG has its seed set when your game starts. If the user specifies a seed in the object properties then it uses that, otherwise it will use the browser PRNG to generate a unique one. During runtime this seed can be read, and changed. It's also possible to generate new unique seeds from the browser PRNG using the RandomSeed expression.

    There's no direct way to get a value from the PRNG in Advanced Random. But as Fib says there is a check box in the Advanced Random properties which changes the PRNG that the system random expressions uses to be the one in Advanced Random. Allowing you access to those random expressions as well.

    So in summary, the PRNG is only predictable if you set the seed to be a predictable one and you can create a new unpredictable seed at any time.

  • So if you want to use a binary packet format you are going to need a well defined structure to it. Anything without a fixed size will need an extra field to tell you how long the data is going to be, otherwise you will not be able to read it back.

    In terms of your chosen data for each peer you have:

    • PeerID (unsure if sized)
    • Position X (sized)
    • Position Y (sized)
    • Direction (sized)

    PeerID is somewhat frustrating, I can't tell from the documentation if it's always a fixed length. If it is then we only need 1 size value for the whole packet, but if it isn't then we need one for each peer! I think we will have to assume that it can vary in length.

    We will need to store an integer value at the start of our packet that contains the number of peers. Then for each peer we need to store 2 integers ( direction, peer id length ), 2 floats (position x, position y,) and then a string ( peer id ).

    For writing strings you will need a second binary data object to write the string into, then copy the contents of that object into the main one. This limitation is due to it being hard to know how many bytes you need to store a string until you try to write it.

    Example serialisation

    1. First we need to calculate how big the buffer needs to be, we need to loop through each peer and add up the sizes of the data. Let's use uint8 for the integers (max value is 255) and float64 for the fractional values. This gives us the size for each peer (2 x 8) + (2 x 1) + (peerIDByteLength) then add the size of the peer count ( 1 x 1 ).

    2. Create a new buffer with the calculated size.

    3. Write the number of peers as a uint8.

    4. Loop through each peer

    4a. Write peer ID length as uint8

    4b. Write peer direction as uint8

    4c. Write peer position X as float64

    4d. Write peer position Y as float64

    4e. Write peer ID as string

    You will have to keep track of the current byte offset while writing the data, you don't want to just keep overwriting the value at index 0. For each uint8 written increate the offset by 1, for float 64 by 8 and for a string the number of characters. Deserialisation is simpler, as we don't have to pre calculate the buffer size. But we will still have to keep track of the byte offset as we read values.

    1. Read peer count as u8.

    2. Repeat by peer count.

    2a. Read peer ID length as uint8

    2b. Read peer direction as uint8

    2c. Read peer position X as float64

    2d. Read peer position Y as float64

    2e. Read peer ID as string, using length from step 2a

    This technique does make a small assumption that you don't have more than 255 peers... You can use a larger integer size if that's a requirement, but somehow I don't think it will be.

  • Obstacle avoidance and attraction is only half of pathfinding. The other half being the defining characteristic of true path finding in my opinion. But I understand your point.

    An example you can consider is if you have a V shaped wall, the entity is inside the V and the target is beyond the point of the V. The attraction will pull the entity toward the bottom of the V, but obstacle avoidance will send it the opposite way. Compare this to path finding where it will immediately know that it cannot reach the destination that way, and will go up and around the top of the V shape.

    The SphereCast stuff is interesting, I'm not sure how that works mathematically. The trouble with rays is that they have a width of 0, so you need to cast an infinite number to simulate line thickness. You can estimate it by casting a few, and hope nothing is in the gaps. It's possible to do volume based ones using signed distance functions (SDF) and ray marching, but it requires a way to generate an SDF for each collision object. It would also be a fair bit slower I wager. A third option would be to do a capsule intersection test with all the collision objects. I'm not really sure what sort of information you'd get back from that, other than it hit something.

  • Try Construct 3

    Develop games in your browser. Powerful, performant & highly capable.

    Try Now Construct 3 users don't see these ads
  • Oh newt you've probably seen this article, but I'll mention it just in case. This is probably the best reference I've found about the different types of pathfinding algorithms redblobgames.com/pathfinding/tower-defense

  • Mmm interesting to think about a waypointing system. You can add in an attraction force, which tells the flock to try and move that way. But if a wall is in the way they will just keep bouncing off the wall instead of going around it like path finding would.

    You could potentially use path finding to build a route, then use that route as unobstructed attraction points. Is that what you were thinking? It might prove to be hard to decide when to move to the next waypoint. Unless each entity followed the waypoints individually, but that might change the flock dynamic. *shrugs* all needs testing to find out really.

  • newt Both valid points, but I don't think either of them would be show stoppers. The collision boxes for any obstacles would need to be synced to the BOID WASM module, but that only needs to occur once per tick before the BOID logic ticks.

    As for actually moving and interacting with solid objects, most movement behaviours work like so if move_does_not_collide(x, y) { move(x, y) }. They will also check if they have somehow become stuck in something since their last tick before that, as movement updates occur independently. The BOID module would do checks for such things, then update the positions of all instances in the engine. So plausibly it would be the same as the existing movement behaviours.

    At worst it would have to behave as the physics behaviour does, where everything is controlled by the physics module. But it could probably avoid that.

  • Hey Badmiracle, yeah sorry all my old dropbox links will be dead. But I still have the files. Try this

    drive.google.com/file/d/1aa5uiZRA70nmnt8iC0f0vbrqgtgfdUrF/view

  • Still a lot of overhead if there's a lot of instances.

    Might be a great candidate for Wasm.

    Anything like this in the works? Nepeo

    So I did a quick profile of the project dop2000 shared, with the instances cranked up to 100. I wasn't surprised to find most of the time was spent raycasting. Depending on how it's used raycasting can be quite expensive ( if you watch the original BOIDs video I'm fairly sure Sebastian had the same problem in unity ). You are effectively checking every line segment against every other one in the possible collision area. Hence you're looking at n^2 behaviour if every entity is checking every other. Ironically the older version of LOS might have been faster here, I think it bailed after the first collision is found, but when raycasting was added it became a requirement to know which collision is the closest so it has to keep going.

    I'm not aware of any work being done on LOS, but I'm somewhat out of the loop. It's plausible that rewriting parts of the collision engine in WASM would improve performance, but there's a cost moving data between JS and WASM ( and there's lots of data involved in the collision engine ). I'd personally be curious what sort of performance a custom made BOIDs plugin written entirely in WASM got.

Nepeo's avatar

Nepeo

Member since 8 Mar, 2016

Twitter
Nepeo has 583,792 followers

Trophy Case

  • 8-Year Club
  • x4
    Coach One of your tutorials has over 1,000 readers
  • x3
    Educator One of your tutorials has over 10,000 readers
  • RTFM Read the fabulous manual
  • Great Comment One of your comments gets 3 upvotes
  • Email Verified

Progress

13/44
How to earn trophies

Blogs