Godot jump animation starting after a slight delay from the input - 2d-games

this is one of my first projects and i'm writing this code following a tutorial on YT. This tutorial does not include stuff about animation as far as i've seen, so i tried getting it done myself and the idle and run animations work. The jump also works but it starts after a slight delay and doesn't complete its cycle because the character lands too soon (for this last problem i'll try adjusting the speed of the animation)
extends Actor
func _physics_process(delta):
var direction = get_direction()
velocity = calculate_move_velocity(velocity, direction, maxSpeed)
velocity = move_and_slide(velocity, FLOOR_NORMAL) #Funzione che permette il movimento del personaggio
func get_direction() -> Vector2:
return Vector2(
Input.get_action_strength("right") - Input.get_action_strength("left"),
-1.0 if Input.is_action_just_pressed("jump") and is_on_floor() else 0.0
)
func calculate_move_velocity( #Movimento e Animazioni
linear_velocity: Vector2,
direction: Vector2,
maxSpeed: Vector2
) -> Vector2:
var new_velocity = linear_velocity #la new_velocity sarà il movimento lineare del personaggio
new_velocity.x = maxSpeed.x * direction.x
new_velocity.x = lerp(new_velocity.x, 0, 0.1)
if is_on_floor() and direction.x == 1.0: #muoversi verso destra
$AnimationPlayer.play("run")
$Sprite.scale.x = 1
elif is_on_floor() and direction.x == -1.0: #muoversi verso sinistra
$AnimationPlayer.play("run")
$Sprite.scale.x = -1
if is_on_floor() and direction.x == 0.0: #stare fermi
$AnimationPlayer.play("idle")
new_velocity.y += gravity * get_physics_process_delta_time()
if direction.y == -1.0: #saltare
new_velocity.y = maxSpeed.y * direction.y
if !is_on_floor() == false and Input.is_action_just_pressed("jump"):
$AnimationPlayer.play("jump")
return new_velocity

I see a lot of little things, will go over them one by one.
Is on floor?
The value is_on_floor() is updated when you call move_and_slide(...). But you are calling is_on_floor() before calling move_and_slide(...), which means it is operating the value of the prior physics frame. In fact, you want move_and_slide(...) to hit the ground (and thus, you probably want to apply gravity first).
This by it self is not a big deal. It is mostly noticeable for frame perfect jump, but still.
Lerp?
I'm looking at this line:
new_velocity.x = lerp(new_velocity.x, 0, 0.1)
Let us see the official documentation for lerp. It says the signature of lerp is:
Variant lerp ( Variant from, Variant to, float weight )
So we are interpolating from new_velocity.x to 0 with a weight of 0.1. I believe that is the same as:
new_velocity.x *= 0.9
Considering that other line also:
new_velocity.x = maxSpeed.x * direction.x
new_velocity.x = lerp(new_velocity.x, 0, 0.1)
We have:
new_velocity.x = maxSpeed.x * direction.x * 0.9
And considering that direction.x goes from -1 to 1, we have that new_velocity.x never reaches maxSpeed.x.
Why you want to do that? What do you think you are accomplishing?
Scale sprite?
You use $Sprite.scale.x = 1 and $Sprite.scale.x = -1. Sprites have a flip_h property strongly encouraged for this use.
Double negative?
You have this little nugget of code: !is_on_floor() == false.
Let us make a truth table:
is_on_floor() │ !is_on_floor() │ !is_on_floor() == false
──────────────┼────────────────┼──────────────────────────
false │ true │ false
true │ false │ true
As you can see, !is_on_floor() == false is the same as is_on_floor(). By the way, it returns bool, not Variant, in case that is your concern.
Playing animation multiple times
As you must be aware, calling AnimationPlayer.play multiple times with the same animation, is no problem at all. Which also means you don't need to check if the jump action was just pressed.
Separation of concerns
On one hand we have the concern of moving the kinematic body. On the other we have the concern of playing the animations. The first concern is a physics concern. It make sense to do it in _physics_process. The second concern is a visual concern. It makes sense to do it in _process instead.
Bringing it all together.
func _physics_process(delta:float) -> void:
velocity.y += gravity * delta
velocity = move_and_slide(velocity, Vector2.UP)
var h_direction = Input.get_action_strength("right") - Input.get_action_strength("left")
velocity.x = maxSpeed.x * h_direction
if Input.is_action_just_pressed("jump") and is_on_floor():
velocity.y = -maxSpeed.y
func _process(_delta:float) -> void:
if is_on_floor():
if velocity.x == 0.0:
$AnimationPlayer.play("idle")
$Sprite.flip_h = false
else:
$AnimationPlayer.play("run")
$Sprite.flip_h = velocity.x < 0.0
else:
$AnimationPlayer.play("jump")
Short and sweet.
Note: I replaced FLOOR_NORMAL with Vector2.UP. Also I removed the 0.9 introduced by lerp. Otherwise this should be the same, minus is_on_floor and animation play timing issues (I think this also differs on walls, there is is_on_wall() if that is an issue). However, we are talking of issues that at most expand one physics frame.
One more thing
Given that you are working with sprites in 2D. Consider using AnimatedSprite for the sprite animations instead of AnimationPlayer.
By the way, the timing of the animation may be the causing an apparent delay. For example if the first sprite of the jump animation matches - say - a frame of the idle animation. That is one whole frame that has to pass before we see a jump sprite. Double check that.

Related

Roblox CFrame lookVector isn't moving part?

I've recently been working a lot with CFrame mechanics while scripting and I've got kind of stuck on this.
Even after using .lookVector, or even Vector3, the Jetpack model position stays equal to the position of the Torso instead of 5 (* -5) behind the torso.
Here is the code I have so far:
local player = script.Parent
local jetpack = game.ReplicatedStorage.Jetpack
local jetpackClone = jetpack:Clone()
jetpackClone.PrimaryPart = jetpackClone.Core
jetpackClone.Parent = player
jetpackClone:moveTo(player.Torso.Position + player.Torso.CFrame.lookVector * -5)
local weld = Instance.new("Motor6D")
weld.Parent = jetpackClone.Core
weld.Part0 = jetpackClone.Core
weld.Part1 = player.Torso
The way to fix this is quite simple.
Like Ahmad said, moveTo is used for models that doesn't have a primary part. But it is not only that.
One thing that Ahmad forgot is that 'lookVector' is not a CFrame, it is a Vector3 instead.
In this fragment, you did
moveTo(player.Torso.Position + player.Torso.CFrame.lookVector * -5)
That would be fine, if you didn't use a numerical value with Vector3's. To fix this, instead, your code should be
local player = script.Parent
local jetpack = game.ReplicatedStorage.Jetpack;
local jetpackClone = jetpack:Clone();
jetpackClone.PrimaryPart = jetpackClone.Core;
jetpackClone.Parent = player; -- Is the 'Player' a Player, or a character??
local weld = Instance.new("Weld", player.Torso) -- We use 'Weld' here, instead of Motor6D
weld.Part1 = jetpackClone.Core;
weld.Part0 = player.Torso;
weld.C0 = CFrame.new(0, 0, -5); -- We use the C0 property of Weld's
That should do it for welding the jetpack to your torso. Though, I would check if the CFrame is correct in it, I am not sure if it is or not, but if the jetpack appears in front of your torso, then replace weld.C0 = CFrame.new(0, 0, -5) to weld.C0 = CFrame.new(0, 0, 5).
Hope my answer helps!
moveTo is used for models that doesn't have a primary part. Which can be inaccurate. Instead use :SetPrimaryPartCFrame() also u were adding Position+CFrame(it would cause an error did u check output?)

Finding the row of an object

I'm trying to create a procedural shape made up of quads.
I want to be able to take any quad and use it's index to find the row that it is in.
Take quad 9 from the image. What sort of function can I use to find the row (in this case it is 2 from a 0-index). What about quad 20?
The rows always change in width by 2 quads, one removed from each side.
Sorry it's a bit convoluted but I'm not sure how to approach the problem.
Assume diameter d and quad number q. I claim the rows go 0 to d-1. Moreover, there are (d/2)(2+d) elements. The easier case is if 0<=q<(d/4)(2+d) in which case we are in the first half. Then the index is trunc((-1+sqrt(1+4*q))/2). This comes from using the observation that row n begins with n(n+1) which could be formally shown with the sum of an arithmetic series, then working backwards and solving the quadratic equation.
On the other hand, if we are in the second half (d/4)(2+d)<=q<(d/2)(2*d) and we solve by taking the offset from the end. Let q' be (d/2)(2+d)-1-q. Apply the above index formula to q' instead of q, and subtract the result from d-1 to get the index of q's row.
I may be off by one here or there, but I think this is the gist of it.
I was thinking since this was posted to a programming site, maybe it would be more logical to give a function one could implement without pulling out a lot of math, and instead just relying on addition. I think it would be easier to follow and harder to mess up (though maybe I underestimate my capability to mess up, and I almost did).
var quadRowIndex = function (diameter, quadNumber) {
//diameter should be a positive even number
//quadNumber should be between 0 and index of last number in last row (inclusive)
var quadIndex = 0; //holds the RowIndex, which the function will return once the row contains quadNumber
var rowStartNum = 0;
var rowLength = 2;
//iterate through first half
while (rowLength <= diameter) {
rowStartNum += rowLength;
if (rowStartNum > quadNumber) {
return quadIndex;
}
quadIndex++;
rowLength += 2;
}
rowLength -= 2;
//iterate through second half if still here
while (rowLength >= 2) {
rowStartNum += rowLength;
if (rowStartNum > quadNumber) {
return quadIndex;
}
quadIndex++;
rowLength -= 2;
}
//still here -- number was too high, return error signal
return -1;
};
console.log(quadRowIndex(6, 9));
console.log(quadRowIndex(6, 20));
console.log(quadRowIndex(6, 100));

Function to generate randomly incrementing number over time

I'm trying to make a fake download count. It should increment randomly over time. Some download-count-like patterns would be nice.
Is this possible without using a database, or storing a counter anywhere?
My idea is to check the number of seconds that have passed since my app was released. Then just throw that into a formula which spits out the fake download count. Users can request to see the download count at any time.
Is there a math function that increments randomly? I could just pass my secondsPassed into there and scale it how I'd like.
Something like this: getDownloadCount(secondsPassed)
Edit: here's an example solution. But it gets worse performance over time.
downloadCount = 0
loop secondsPassed/60 times // Loop one more time for every minute passed
downloadCount += seededRandom(0, 10)
Making a fake download count doesn't sound like a nice thing to do. However in designing secure communication protocols, there are legitimate use cases for monotonically growing functions with some randomness in their values.
I am assuming you have:
A growth model given as a monotonically growing function providing approximate values for the desired function.
Access to a time stamp, which never decreases.
Ability to store a constant random seed along with the function definition.
No way to store any updated data upon the function being queried.
First you decide on a window length, which will control how much randomness will be in the final output. I expect you will want this to be on the order of one hour or a few.
Figure out which window the current time is within. Evaluate the reference function at the start and end of this window. Consider the rectangle given by start and end time of the window as well as min and maximum value given by the reference function. Feed the corners of this rectangle and your constant seed into a PRNG. Use the PRNG to choose a random point within the rectangle. This point will be on the final curve.
Perform the same computation for one of the neighbor windows. Which neighbor window to use depend on whether the first computed point on the curve is to the left or the right of the current time.
Now that you have two points on the curve (which are reproducible and consistent), you will have to iterate the following procedure.
You are given two points on the final curve. Consider the rectangle given by those corners. Feed the corners and your constant seed into a PRNG. Use that PRNG to chose a random point within the rectangle. This point will be on the final curve. Discard one of the outer points, which is no longer needed.
Since the Y-values are restricted to integers, this procedure will eventually terminate once your two points on the curve have identical Y-coordinate, and you will know, that the function has to be constant between those two points.
You could implement a Morris Counter.
It works like this: start off by setting the counter to 1. Each time you want to increase the count (which could be every iteration of some loop or every time an event happens, but does not need to be determined randomly), then you do a random procedure to determine the effect it has on the counter.
It can have possibly no effect, or it can have the effect of raising the order of magnitude of the count. The probability is based on whether or not n successive fair coin flips all turn up heads, where n is the number of bits needed to encode the current counter value in binary. As a result, once the counter has gotten pretty high, it's very hard to make it go even higher (the state of the counter models a phenomenon where by you are already way overestimating the count, so now you need lots of nothing-happens events to compensate, making the count more accurate).
This is used as a cheap way to store an approximate count of a very large collection, but there's no reason why you can't use it as your randomly increasing counter device.
If you want better accuracy, or you want the count outputs to be more "normal" numbers instead of always powers of 2, then you can just create several Morris Counters, and at each step average together the set of current counts across them all.
You are after a sequence which always increases by a random amount, depending on how long you last requested the sequence.
This can be done through a random sequence that is always seeded the same.
Then we iterate through the same sequence each time to get the graph.
We need a function that increments our counter, store the new Time and Count and return the count.
Ideally we would model the increases as a poisson process, but a linear one here will do.
class Counter {
private static int counter = 0;
private static int time = 0;
private static double rate = 5.0;
private Random r;
public Counter(int seed){
counter = 0;
r = new Random(seed);
}
private int poisson(double rate, int diff){
// We're gonna cheat here and sample uniformly
return r.Next(0, (int)Math.Round(rate * diff));
}
public int getNext(int t){
var diff = t - time;
time = t;
if (diff <= 0) return counter;
counter += this.poisson(rate, diff);
return counter;
}
}
void Main()
{
var c = new Counter(1024);
for(var i = 0; i< 10; i++){
Console.WriteLine(String.Format("||{0}\t|{1}\t||",i,c.getNext(i)));
}
}
This outputs (for example):
||t |hit||
||0 |0 ||
||1 |3 ||
||2 |4 ||
||3 |6 ||
||4 |6 ||
||5 |8 ||
||6 |10 ||
||7 |13 ||
||8 |13 ||
||9 |16 ||
For some deterministic function f, (perhaps f(x) = x, or if your fake app is REALLY awesome f(x) = 2^x), and a random function r which outputs random number that's sometimes negative and sometimes positive.
Your graphing function g could be:
g(x) = f(x) + r
EDIT
How about this: https://gamedev.stackexchange.com/questions/26391/is-there-a-family-of-monotonically-non-decreasing-noise-functions
Well it's not "random" but you could use A*(X/B + SIN(X/B)) (scaled by some number) to introduce some noise. You can adjust A and B to change the scale of the result and how often the "noise" cycles.
Really, any periodic function that has a first derivative within some bounds could work.
as quick solution you can use something like this (code in java):
static long f(final int x) {
long r = 0; // initial counter
long n = 36969L; // seed
for (int i = 0; i <= x; i++) {
n = 69069L * n + 1234567L; // generate Ith random number
r += (n & 0xf); // add random number to counter
}
return r;
}
by playing with numbers 36969L and 0xf you can achieve different results
numbers 69069L and 1234567L are from standard LCG
the main idea - create simple random, with the same seed and for every passed x (number of seconds) replay random additions to counter
A good model for random events like downloads is the Poisson distribution. You need to estimate the average number of downloads in a given time period (hour, say) and then invert the Poisson distribution to get the number of downloads in a time period given a uniformly distributed random number. For extra realism you can vary the average according to time of day, time of week, etc. Sample algorithms are available at http://en.m.wikipedia.org/wiki/Poisson_distribution#Generating_Poisson-distributed_random_variables.
Here is a javascript implementation of a "fake" download-counter that appears the same to everyone. This always returns the same results for everyone each time and doesn't require database or files to do that. It also gracefully handles the case where you don't ask for new data at the same time, it will still look natural next time you request a day.
https://jsfiddle.net/Lru1tenL/1/
Counter = {
time:Date.now(),
count:0,
rate:0.45
};
Counter.seed = function(seed, startTime)
{
this.time = startTime,
this.count = 0,
this.prng = new Math.seedrandom(seed);
this.prng.getRandomInt = function(min, max) {
return Math.floor(this() * (max - min)) + min;
};
};
Counter.getNext = function(t){
var diff = t - this.time;
console.log(diff);
if(diff <= 0) return this.count;
this.time = t;
var max = Math.ceil(diff/100 * this.rate);
console.log("max: " + max);
this.count += this.prng.getRandomInt(0,max);
return this.count;
};
var results = [];
var today = Date.now();
Counter.seed("My Random Seed", today);
for (var i = 0; i < 7; i++) {
if(i === 4)
{
results.push(null);
} else {
var future = today + 86400000 * i;
results.push(Counter.getNext(future));
}
}
console.log(results);
var data = {
labels: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday","Sunday"],
datasets: [
{
label: "My Second dataset",
fillColor: "rgba(151,187,205,0.2)",
strokeColor: "rgba(151,187,205,1)",
pointColor: "rgba(151,187,205,1)",
pointStrokeColor: "#fff",
pointHighlightFill: "#fff",
pointHighlightStroke: "rgba(151,187,205,1)",
data: results
}
]
};
var ctx = document.getElementById("myChart").getContext("2d");
var myLineChart = new Chart(ctx).Line(data);
Is the javascript. It creates a counter object that increments when requested based on the time of the previous value asked for. The repeatability comes through a thirdparty library "seedrandom" and the chart is drawn using chartjs.
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/1.0.2/Chart.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/seedrandom/2.4.0/seedrandom.min.js">
</script>
<body>
<canvas id="myChart" width="600" height="400"></canvas>
</body>
</html>
You can use Unix timestamp. Something like:
Downloads = constant + ( unix time / another constant )
You can vary both constants to get a reasonable number.
P.S: Thats if you want a linear function, otherwise you can do:
Downloads = (unix time) ^ constant
and so on.

How to suitably avoid RangeErrors when "looking around" this 2D array?

I have a 2D array structure to represent a grid of tiles that is a part of the game I am making. One aspect of the game is that the grid is filled in in a somewhat random fashion, based on analysis of a text file. Right from the outset though, I already realised that just leaving it be pretty much randomly done like this without sticking in some kind of validity checks or prevention mechanism, to stop really badly configured grid from forming, would not work out. The main problem I want to avoid is too many tiles that would be untraversable being close together, potentially severing chunks of the grid from the rest.
The idea I came up with to try avoid some really bad grids is to check when assigning a tile value to each "grid square" during generation with logic like this
if (tileBeingInserted.isTraversable()) {
//all is well
return true;
} else {
//we may have a problem, are there too many untraversables nearby?
//Proceed to check all squares "around" the current one.
}
To be clear, checking around the current square means checking the square immediately adjacent in each of the 8 cardinal directions. Now, my problem is that I am trying to reason out how to code this so that it will certainly not give a RangeErrorat any point or at least catch it and recover if it must. As an example, you could clearly take one of the corner squares to be the worst scenario in the sense that only 2 of the squares the algorithm would want to check are within the array's bounds. Naturally, if a RangeErrorhappens for this reason I just want the program to progress onward without issue so the structure
try {
//check1
//check2...8
} catch (RangeError e) {
}
is unacceptable because as soon as a single out of range square is tested the code falls out of the check block. An alternative I thought of, but do not like because of its messiness, would be to individually wrap each check in a try-catch and yes that would work I guess but that's some horrid looking code...so can anyone help me out here? Is there perhaps a different angle from which to come at this problem of avoiding the RangeErrors that I am not seeing?
So my code for testing whether another untraversable tile should be placed has shaped up like this:
bool _tileFitsWell(int tileTypeInt, int row, int col)
{
//...initialise some things, set stuff up
...
if (tile.traversable == true) {
//In this case a new traversable tile is being put in, so no problems.
return true;
} else {
//begin testing what tiles are around the current tile
//Test NW adjacent
if (row > 0 && col > 0) {
temp = tileAt(row - 1, col - 1);
if (!temp.traversable) {
strikeCount++;
}
}
//Test N adjacent
if (row > 0) {
temp = tileAt(row - 1, col - 1);
if (!temp.traversable) {
strikeCount++;
}
}
//Test NE adjacent
if (row > 0 && col < _grid[0].length - 2) {
temp = tileAt(row - 1, col 1);
if (!temp.traversable) {
strikeCount++;
}
}
//Test W adjacent
if (col > 0) {
temp = tileAt(row, col - 1);
if (!temp.traversable) {
strikeCount++;
}
}
}
return strikeCount < 2;
}
The code inside each "initial" if-statement (the ones that check row and col) is a bit pseudocode-ish for simplicity's sake. As I explained in a previous comment, the reason why I don't need to check tiles in the other 4 cardinal directions is since these checks are done while filling the map, tiles in those positions will always be either uninitialised or just out of bounds, depending on what tile the function is called to check at a given time.

Flex: getting the height of a collection of controls

Or putting it more accurately, I want to be able to get the distance between the top of a control to the top of one of its children (and adding the height member of all the above children yields specious results!) but the process of getting the absolute coordinates, and comparing them, looks really messed up.
I use this function to calculate the height between the tops of 2 tags:
private static function GetRemainingHeight(oParent:Container, oChild:Container,
yParent:Number, yChild:Number):Number {
const ptParent:Point = oParent.localToGlobal(new Point(0, yParent));
const ptChild:Point = oChild.localToGlobal(new Point(0, yChild));
const nHeightOfEverythingAbove:Number = ptChild.y - ptParent.y;
trace(ptChild.y.toString() + '[' + yChild.toString() + '] - ' +
ptParent.y.toString() + '[' + yParent.toString() + '] = ' + nHeightOfEverythingAbove.toString() + ' > ' + oParent.height.toString());
return nHeightOfEverythingAbove;
}
Note that oParent.y == yParent and oChild.y == yChild but I did it this way for binding reasons.
The result I get is very surprising:
822[329] - 124[0] = 698 > 439
which is impossible, because the top of oChild does not disappear below oParent. The only figure I find unexpected is ptChild.y. All the other numbers look quite sane. So I'm assuming that my mistake was in subtracting two figures that are not supposed to be comparable.
Of course, if anyone has a method of calculating the difference between two points that doesn't involve localToGlobal(), that'd be fine, too.
I'm using the 3.5 SDK.
I found a partial answer by looking to http://rjria.blogspot.ca/2008/05/localtoglobal-vs-contenttoglobal-in.html (including the comments). It dithers on whether or not I should be using localToGlobal() or contentToGlobal(), but it filled in some blanks that Adobe's documentation left, which is that you get the global coordinates by feeding the function new Point(0, 0). In the end, I used this:
public static function GetRemainingHeight(oParent:DisplayObject, oChild:DisplayObject,
yParent:Number, yChild:Number):Number {
const ptParent:Point = oParent.localToGlobal(new Point(0, 0));
const ptChild:Point = oChild.localToGlobal(new Point(0, 0));
const nHeightOfEverythingAbove:Number = ptChild.y - ptParent.y;
return nHeightOfEverythingAbove;
}
See question for an explanation for the seemingly unnecessary parameters, which now seem like they might really be irrelevant.
However, I didn't need this function as often as I thought, and I'm not terribly happy w/the way it works anyway. I've learned that the way I've done it, it isn't possible to just make all those parameters to the function Bindable and expect this function to be called when changes to oChild are made. In one case I had to call this function in the handler for the updateComplete event.

Resources