Jump to content

Skillsbo

Member
  • Posts

    12
  • Joined

  • Last visited

  • Days Won

    5

Everything posted by Skillsbo

  1. What? This tutorial will be outlining the process and steps of writing my Yandu's Arrow script to hopefully teach others about what is possible in Skript with few lines of code. (If you don't know who Yandu is, he's some blue bad/good guy in the Guardians of The Galaxy movies who uses a magic arrow and it flies through a handful of enemies by itself) So that's basically what we will be creating today The Start So where would I start? I would personally observe how the arrow acts in the movies, you can see that it flies through an enemy once to get the ones behind/next to and each one (mostly) never gets hit more than once. So how could we emulate this behavior? You might think that just doing them based on the distance to you would be the answer, and you would be right, ish, it would get the job done but it won't not look as clean (see below) So if that's not quite what we want, what's the better option? What if we still sort by distance but not by the player? What if we first get the closest zombie to the player, and then get the closest one to that one and then rinse and repeat? That would produce a much better looking result (see below) Now that we know how we should sort the zombies we can move onto actually writing the script. Writing The Script So we can begin writing the script now, we can start by adding the main event, for me I'll just make it activate when you right click with an arrow with a special name and it will just spawn an arrow with no gravity (prevent it from falling) on right click with arrow: if name of player's tool = "&9Yandu's arrow": spawn 1 arrow at player set gravity of last spawned arrow to false So now, how do we get the zombie that's closest to the player? Sadly `all x in radius y of z` does not return the entities in order of distance we will have to make our own part that does that. This is a simple sorting thing, it loops through all the (zombies in this case) in radius 20 and compares the distance to the other ones and the resulting closest zombie is in `{_z}` set {_d} to 21 loop all zombies in radius 20 of player: if distance between loop-zombie and player < {_d}: set {_z} to loop-zombie set {_d} to distance between loop-zombie and player So now we need to sort all the zombies in the order we discussed earlier, one way to do this is to use recursion as it greatly reduces the amount of lines we need (recursion basically means defining something in terms of itself) So we put this in our main event (keep in mind that the `{_t::*}` variable has not been set and we want it that way) set {_z::*} to closestZombie({_z}, {_t::*}) And now we need to define the `closestZombie` function function closestZombie(a: entity, t: entities) :: entities: set {_d} to 100 loop all zombies in radius 20 of {_a}: if {_t::*} does not contain loop-zombie: if distance between loop-zombie and {_a} < {_d}: set {_z} to loop-zombie set {_d} to distance between loop-zombie and {_a} add {_z} to {_t::*} add {_z} to {_z::*} set {_c::*} to closestZombie({_z}, {_t::*}) add {_c::*} to {_z::*} return {_z::*} Basically the function gets the closest zombie to the current zombie and then gets the closest zombie to that one and so on and so forth while also keeping track of which zombie has been already "taken". So once we have that defined we can move on the final part (the visuals) loop size of {_z::*} times: set {_i} to loop-value-1 + 1 if {_i} <= size of {_z::*}: loop 5 times: set {_l} to linearBezier((({_z::%loop-value-1%} ~ vector(0, 1.5, 0)) and ({_z::%{_i}%} ~ vector(0, 1.5, 0))), loop-value-2 / 5) play 5 of flame at {_l} offset by vector(0, 0, 0) with extra .05 set {_v} to vector between {_z::%loop-value-1%} and {_z::%{_i}%} set vector length of {_v} to .01 teleport last spawned arrow to {_l} set velocity of last spawned arrow to {_v} wait 1 tick kill {_z::%loop-value-1%} kill {_z::%loop-value-1%} kill last spawned arrow The code may look daunting at first but its pretty simple, we loop through the list of zombies (that are now in order thanks to our epic sorting function) then we check to make sure we are not at the second to last zombie (to make sure the visuals play out nicely between the zombies) and then we use another function called `linearBezier` which basically just means a line from A to B at the position t (time) that will make us a line to where we can teleport the arrow to and create a few particles to add a little pizzazz, we use a vector here just to make the arrow face in the right direction, its nothing too fancy. function definition for `linearBezier` below function linearBezier(l: locations, t: number) :: location: set {_x} to (1 - {_t}) * x-loc of {_l::1} + {_t} * x-loc of {_l::2} set {_y} to (1 - {_t}) * y-loc of {_l::1} + {_t} * y-loc of {_l::2} set {_z} to (1 - {_t}) * z-loc of {_l::1} + {_t} * z-loc of {_l::2} return location({_x}, {_y}, {_z}, world of {_l::1}) The End Slapping it all together and we get function linearBezier(l: locations, t: number) :: location: set {_x} to (1 - {_t}) * x-loc of {_l::1} + {_t} * x-loc of {_l::2} set {_y} to (1 - {_t}) * y-loc of {_l::1} + {_t} * y-loc of {_l::2} set {_z} to (1 - {_t}) * z-loc of {_l::1} + {_t} * z-loc of {_l::2} return location({_x}, {_y}, {_z}, world of {_l::1}) function closestEntity(a: entity, t: entities) :: entities: set {_d} to 100 loop all zombies in radius 20 of {_a}: if {_t::*} does not contain loop-zombie: if distance between loop-zombie and {_a} < {_d}: set {_z} to loop-zombie set {_d} to distance between loop-zombie and {_a} add {_z} to {_t::*} add {_z} to {_z::*} set {_c::*} to closestEntity({_z}, {_t::*}) add {_c::*} to {_z::*} return {_z::*} on right click with arrow: if name of player's tool = "&9Yandu's arrow": spawn 1 arrow at player set gravity of last spawned arrow to false set {_d} to 21 loop all zombies in radius 20 of player: if distance between loop-zombie and player < {_d}: set {_z} to loop-zombie set {_d} to distance between loop-zombie and player set {_z::*} to closestEntity({_z}, {_t::*}) loop size of {_z::*} times: set {_i} to loop-value-1 + 1 if {_i} <= size of {_z::*}: loop 5 times: set {_l} to linearBezier((({_z::%loop-value-1%} ~ vector(0, 1.5, 0)) and ({_z::%{_i}%} ~ vector(0, 1.5, 0))), loop-value-2 / 5) play 5 of flame at {_l} offset by vector(0, 0, 0) with extra .05 set {_v} to vector between {_z::%loop-value-1%} and {_z::%{_i}%} set vector length of {_v} to .01 teleport last spawned arrow to {_l} set velocity of last spawned arrow to {_v} wait 1 tick kill {_z::%loop-value-1%} kill {_z::%loop-value-1%} kill last spawned arrow I might do more of these later one with other cool concepts (maybe laser beams will be next)
  2. Hopefully this will be a complete tutorial in vectors within vanilla Skript (might add onto this with Biosphere stuff) Terms you will learn (definitions of each one will be in the tutorial) : Pitch and yaw Vector Magnitude Normalization Location Velocity Basic arithmetic Dot product Cross product Pitch and yaw Pitch and yaw define the Cartesian rotations in Minecraft. Yaw is the horizontal rotation and pitch is the vertical rotation, they are used to describe your facing direction (rotation). Minecraft displays your yaw going from 0 (south) to 180 (north), then -180 (north) back to 0 (south) And it displays your pitch going from 90 (looking down) to -90 (looking up) Now how is pitch and yaw related to vectors? Vectors Vectors are a way to describe both direction and magnitude, they do not describe location. Imagine vectors as an arrow in 3D space, the head of the vector is the tip of the arrow and the tail is the longer part. The head you can think of as "the direction that the vector is facing or heading" and you can think of the tail of the vector as how "strong" the vector is or the magnitude of the vector. You can create a basic vector from the yaw and pitch using the following code set {_v} to vector from yaw 0 and pitch 0 or from individual X, Y and Z components with set {_v} to vector(x, y, z) Magnitude Magnitude is the "length", "force" or "strength" of a vector, which you can think of as the length of the tail. It describes how long it is which is used to describe how long the vector is. Also magnitude/length is independent of direction (I know it's a great definition, if you don't get it now it will be made more clear soon) You can set the length of a vector using the following line set (standard|vector) length of {_v} to 5 and you can get the length with (standard|vector) length of {_v} Normalization Normalization is the way of taking a vector from its original length and making it 1. Length is independent of direction, so you can change the length all you want and its direction will not change. You can normalize a vector in 2 ways set {_v} to normalized {_v} # or set (standard|vector) length of {_v} to 1 Locations As stated earlier, vectors describe both direction and magnitude and not location, but they can be used to offset locations (offsetting a location by a direction). Think of offsetting like taking the vector, putting the tail at a location, and then wherever the head is is where the resulting location will be (and this is where magnitude also comes in, it will be how far the offset location is from the original location) You can offset a location by using [set {_loc} to] {_loc} (~|offset by) {_v} Velocity Since vectors describe both direction and magnitude, they are used to describe the velocity of entities or objects. Velocity is the speed of something in a given direction (wow it's like its almost the same definition of a vector). You can get the velocity of something using velocity of %(player|entity)% and you can set the velocity of something with set velocity of {_something} to {_v} # or set velocity of {_something} to vector(0, 1, 0) Vector Arithmetic Just like doing basic math with normal numbers (like 5 + 5 = 10), we can do basic math with vectors (notice the double signs). # Addition set {_v} to {_v} ++ {_v2} set {_v} to vector(1, 2, 3) ++ vector(4, 5, 6) # Subtraction set {_v} to {_v} -- {_v2} set {_v} to vector(1, 2, 3) -- vector(4, 5, 6) # Multiplication set {_v} to {_v} ** {_v2} set {_v} to vector(1, 2, 3) ** vector(4, 5, 6) # Division set {_v} to {_v} // {_v2} set {_v} to vector(1, 2, 3) // vector(4, 5, 6) Just in case you wanted to know what's going on behind the arithmetic, they are just doing the operations between the X, Y and Z components like this (addition for example) : # Addition vector(1, 2, 3) vector(4, 5, 6) + _______________ vector(5, 7, 9) Dot Product Doing the dot product of 2 vectors will return a number between 0 and 1 based on how similar 2 vectors are regardless of magnitude (0 meaning facing away and 1 meaning they are the same direction). You can get the dot product of 2 vectors using {_v} dot {_v2} Order of the vectors does not matter The image below outlines 3 examples of dot product with 2 vectors (magenta and light blue) and what it would return Cross Product Cross product is used to find the vector perpendicular to 2 other vectors crossing a plane Vector Rotations You can also rotate vectors around other vectors and vectors around the global X, Y and Z axis Rotating a vector along one of the global axes can be done using the following code # Rotating {_v} around the global x axis by 90 degrees rotate {_v} around x-axis 90 Which will look something like this And you can also rotate a vector around another vector using # Rotating {_v} around the direction of {_v2} by 90 degrees rotate {_v} around {_v2} by 90 Which will look something like this ({_v} is the light blue vector and {_v2} is the magenta) Miscellaneous Stuff (stuff not in other sections) Vector Yaw and Pitch You can get and set the yaw and pitch of a vector using the following (note that when setting the vector length will be set to 1) # Get vector yaw of {_v} # Set set vector yaw of {_v} to 15 X, Y and Z Components You can get and set the individual X, Y and Z from vectors using the following (note when setting, the vector length is subject to changing) # Get x of {_v} # Set set x of {_v} to 15 Vector Between Locations You can get a vector from 2 locations using the following (it will return a vector from the first location facing the second with the length equal to the distance between both locations) set {_v} to vector from {_l} and {_l2} Angle Between 2 Vectors You can get the angle between 2 vectors (their tails) using the following set {_a} to angle between {_v} and {_v2} And that would look something like this (assuming the vectors are perpendicular like shown) Usage Example (guns) The following script is an example of a use for vectors in Skript on right click with cake: set {_l} to location of player set {_v} to vector from yaw player's yaw and pitch player's pitch loop 40 times: set standard length of {_v} to loop-value / 2 set {_pewpew} to {_l} ~ {_v} kill all players in radius 1.5 of {_pewpew} where [input is not player] # show some epic particles at {_pewpew} To break it down, basically we store the location of the player, then we create a vector from their rotation, then we loop and increase the length of the vector and finally we offset the player's location by the vector and then bonk any players near the "bullet" that are not the player that initially shot
  3. Out of boredom I created a library that allows you to easily draw in 2D and in 3D on maps (and you can even animate it) Only requirements are Skript (duh) and Skellet Links Script: https://pastebin.com/raw/raHinyEV Documentation (it's a Google Doc because I'm lazy): https://docs.google.com/document/d/1T0uYPSHtfQVMXpez1zM_cyHGsU_FTCGaF0FTGPdtfXc/edit?usp=sharing You can create something like this or even cooler! (it's not this choppy) Because making this script took WAY longer than expected, credit is appreciated So yea... that's about it...
  4. You lied to me. It's 1609.344
  5. Don't ever center your text again here
  6. Skillsbo

    Testing

    Epic new forum
×
×
  • Create New...