PRPL 102: Moving an Emitter (As requested by yum234)

Started by GoodMorning, December 01, 2016, 05:58:30 PM

Previous topic - Next topic

GoodMorning

For the purposes of this topic, we will assume that you have an Emitter (or other unit/s) on a blank map, which you wish to move around. Here's a guide to doing this with PRPL.

Assumed knowledge: Nothing that you won't be able to find in the CRPL overview. (Well, you need to know how to work the map editor, but...)




First, we need to know exactly what we need to do. "Build a ladder to the moon" is simple as a sentence, but actually doing it is not.
We need to:

  • Locate the Emitter(s) which we want to move.
  • Find a point to move it to.
  • Actually move it.

Fortunately, Virgil has done most of the hard work for us.




Step 1: Locating the Emitter.
This only needs to be done once, so we use a "once" block. We'll assume that the PRPL Core is directly over the Emitter(s).

Virgil has provided a function GetAllUnitsInRange, which is useful here. It takes four arguments. The first two put on the stack are the coordinates where the command starts looking. The next is the range. The final one relates to whether the we look for units in a circle or a square.

In most cases we will have other Emitters on the map. Where do we look for the one we want? For this script, we'll assume that the Core which is running this script will be sitting directly over the Emitter(s) which we want. The CurrentCoords command gets this location for us.

We only want Emitters which are on exactly the same spot, so the range is 0. We can make the code slightly more efficient by doing a box-search, which means that we supply 1 as the last argument to GetAllUnitsInRange.

Putting it all together, this is the first part of the script.

once
CurrentCoords 0 1 GetAllUnitsInRange ->Units
endonce





Step 2: Where do we want to move it?
There are various ways to talk about this section. I could blather on about parametric forms of oriented curves in 2-space, but I'll use the programmers form, or "easy way".

For a simple movement, we will move back and forth along a line. We'll move, say, up to 400 pixels left and right of where we started. We can do more complicated things, but this is a good start.

Moving based on the time is easy, but the time since when? Let's start our clock in that once block we defined already. It needs to tick every frame (i.e. increase by 0.01), so we'll do that too. While we're at it, we'll need to know where we started.

So, the next version of the code:

once
CurrentCoords 0 1 GetAllUnitsInRange ->Units
0 ->Clock
CurrentPixelCoords ->StartY ->StartX
endonce


<-Clock 0.01 add ->Clock


Now, we need to actually work out where the Emitter needs to go. We'll use the sine function for positioning, because it looks good and will work easily. It's easier to show visually what this will do, so bear with it for now.

A sine curve will be between 1 and -1, inclusive, depending on the number passed in. If we multiply this by 400 (our maximum pixel offset), we'll get the offset from the starting position which we want. We can the add this to the starting position, and we'll get the adjusted X position. We won't change Y at the moment.

So, our code becomes:

once
CurrentCoords 0 1 GetAllUnitsInRange ->Units
0 ->Clock
CurrentPixelCoords ->StartY ->StartX
endonce

<-Clock sin <-StartX add ->NewX
<-StartY ->NewY


<-Clock 0.01 add ->Clock





Step 3: Moving the Emitter(s)
Now we have the position, we need to move the Emitters. Fortunately, Virgil allows us to set the coordinates of units. We will move everything we found at the start, including Emitters, Omnis, caches, spawners, PRPL Cores, ...; the lot. To achieve this, we will use a do loop. We begin at position 0 and stop when we reach the end of the list. Note that <-List[<-Index] is the PRPL shorthand for accessing the element of List at position Index, and that I is the counter of the do loop. To better understand I, look at CRPL, which is better documented. We'll use the Accessor Notation (AcNot) form, because it is easier to read. Essentially , this means that instead of writing... <-Unit CONST_UNITPIXELX <-NewX SetUnitAttribute ...we can write... <-NewX ->Unit.UnitPixelX

Our latest script:

once
CurrentCoords 0 1 GetAllUnitsInRange ->Units
0 ->Clock
CurrentPixelCoords ->StartY ->StartX
endonce

<-Clock sin 400 mul <-StartX add ->NewX
<-StartY ->NewY


<-Units GetListCount 0 do
<-Units[I] ->UID
<-NewX ->UID.UnitPixelCoordX
<-NewY ->UID.UnitPixelCoordY
loop

<-Clock 0.01 add ->Clock


At this point we're probably done. Our unit moves, and goes where we wanted. But it's a little dull, and the script is hard-coded. What if we wanted to reuse it, slightly differently?




Step 4: Adding extras.
Many more things are possible, and here are some of the quickest:



  • Making a circling unit.

  • Making this adaptable.




Circling:

The sine function is inherently related to circles, and it has a similar and useful friend: the cosine. We can use this for a Y offset, and have our unit move in a circle. If we used sine for both X and Y, then we would end up offsetting by the same amount in both directions, and move in a diagonal line.

Replace this:

<-StartY ->NewY


With this:

<-Clock cos 400 mul <-StartY add ->NewY





Adaptability:

This script is all very well, but what if we want to go 800 pixels out? Or 1000? What if we want a number of different range sizes on the same map? Or to start the "clock" at 100 frames in? Or to move 10x as fast? We can use input variables. We set these whenever we run a new copy of the script, and they don't have to be the same every time.


$ClockStart:0
$Range:400
$Speed:0.01

once
CurrentCoords 0 1 GetAllUnitsInRange ->Units
<-ClockStart ->Clock
CurrentPixelCoords ->StartY ->StartX
endonce

<-Clock sin <-Range mul <-StartX add ->NewX
<-StartY ->NewY


<-Units GetListCount 0 do
<-Units[I] ->UID
<-NewX ->UID.UnitPixelCoordX
<-NewY ->UID.UnitPixelCoordY
loop

<-Clock <-Speed add ->Clock





At this point, I'm probably running close to the character limit for the forum, so I'll stop. The attached map has the "adaptable" form of the script, running with defaults. It doesn't have the "circling" addition, because I was testing.

Enjoy.
A narrative is a lightly-marked path to another reality.

Stickman

=====> This is a moderately pointy stick. You need to poke me with it once in three days if you need PRPL from me

Relli

This is excellently done, thank you for taking the time to explain how it all comes together. I'm already a coder, and you taught me a few new things with that. Especially about Sin and Cos.

GoodMorning

For the sine and cosine (sin and cos)...

Imagine that you have a circle, with a "stick" from the middle to the right (length of 1). Imagine that the stick is slowly rotating around the central point, first to the top, then the left, and so on...

The angle that the stick has traversed is what we take the sine and cosine of.

If we look at the circle edge-on from the right, then the sine is the height of the end of the stick. If it is negative, it is below the middle, if positive, above. The stick starts off horizontal, so the sine of zero is 0.

The cosine is similar, but we look at the circle from the top. The cosine is the distance right of the middle (i.e. it begins at 1). If it's negative, then the end of the stick is left of the middle.

So, for those who want coordinates (as we did above) the end of the stick is at (cos(A),sin(A)) where A is the angle that the stick has travelled (in radians). If the circle is centred at at (M,N), the coordinates are (M+cos(A), N+sin(A)).

There is more, but there are better explanations elsewhere.
A narrative is a lightly-marked path to another reality.

Sorrontis

"If you want others to be happy, practice compassion. If you want to be happy, practice compassion."

GoodMorning

Here, we are just teleporting small distances. You can teleport to any point. There's also a command TeleportParticle.
A narrative is a lightly-marked path to another reality.

Sorrontis

Quote from: GoodMorning on January 03, 2017, 12:36:48 AM
Here, we are just teleporting small distances. You can teleport to any point. There's also a command TeleportParticle.

How... How did I not understand that.... Thanks
"If you want others to be happy, practice compassion. If you want to be happy, practice compassion."

GoodMorning

Quote from: Sorrontis on January 03, 2017, 12:42:00 AM
How... How did I not understand that.... Thanks
I must have omitted that from the the original post, though I thought it was in there somewhere. Oh well, since there is no longer a problem, no reason to read through and change anything.
A narrative is a lightly-marked path to another reality.

FOXX

Ooooo this is beautiful, i have to look more into the Forum to discover new things.
I am going to look at this more in a later stage.
But i can see this in one of my Maps.

Nicely Done GM :)
The FPAF
For My Maps and Ships!
Including  CUSTOM MODULE MAPS & SHIPS