movementspeed not updating in a state machine - gml

THIS IS ALL IN THE STEP EVENT.
I am currently trying to create my first state machine for some enemy AI. Being very new to gml and gamemaker studio 2, my code is very basic as I do not know how to implement built in functions.
In 1 of my states, the rush state, the enemy is supposed to chase the player. I do this by creating some conditions, if the player is to the left of the enemy, the enemy will run left. If the player is to the right, the enemy will run right. This in theory is what I have coded but when I get in game, sometimes it will work, but then spasm and go the other way.
If my character is in the circle, it doesn't run towards the player but instead away. This does not change if I reverse the conditions.
case behaviour.rush:
{
//radius in square
if (point_in_circle(playerObject.x,playerObject.y,x,y,200))
{
//direction to face player
if (behaviourState == behaviour.rush && playerObject.x >. warriorx) hsp = -4;
else if (behaviourState == behaviour.rush &&
playerObject.x <= warriorx) hsp = 4;
x = x + hsp;
}
if (!point_in_circle(playerObject.x,playerObject.y,x,y,200))
{
behaviourState = behaviour.idle;
}
}
My full code:
image_speed = 1;
vsp = vsp+grv;
Print(behaviourState);
if (hsp > 0) image_xscale = 3; else if (hsp < 0) image_xscale = -3;
//animation
if (behaviourState == behaviour.idle) sprite_index = tikiAxeWarriorIdle;
if (hsp == 2 || hsp == -2)sprite_index = tikiAxeWarriorWalk;
if (hsp == 4 || hsp == -4) sprite_index = tikiAxeWarriorRush;
if (point_in_circle(playerObject.x,playerObject.y,x,y,200))
{
Print("in circle");
}
Print(hsp);
switch(behaviourState)
{
case behaviour.idle:
{
//stand still
if (alarm[1] <= 0) alarm[1] = room_speed * 4;
if (point_in_circle(playerObject.x,playerObject.y,x,y,200))
{
behaviourState = behaviour.rush;
}
}
case behaviour.wander:
{
if (alarm[0] <= 0) alarm[0] = room_speed * 3;
//move
if (!place_meeting(x + hsp,y,wallObject))
{
x = x + hsp;
}
if (place_meeting(x + hsp,y,wallObject))//checking frame before.
{
hsp = -hsp;
}
//check for behaviour.rush.
if (point_in_circle(playerObject.x,playerObject.y,x,y,200))
{
behaviourState = behaviour.rush;
}
}
case behaviour.rush:
{
//radius in square
if (point_in_circle(playerObject.x,playerObject.y,x,y,200))
{
//direction to face player
if (behaviourState == behaviour.rush && playerObject.x > warriorx) hsp = -4;
else if (behaviourState == behaviour.rush && playerObject.x <= warriorx) hsp = 4;
x = x + hsp;
}
if (!point_in_circle(playerObject.x,playerObject.y,x,y,200))
{
behaviourState = behaviour.idle;
}
}
case behaviour.attack:
{
//attack.
//if player is hit, he dies.
}
}
The expected result is for the enemy to "rush" towards my player position and when over stepped, to face the other way. If my player leaves the circle, it should go back to the idle state.

hsp = -hsp sounds very sensitive to the spasm. and since it's affect the hsp as whole, it'll also affect the place_meeting(x + hsp,y,wallObject) part.
I don't know the solution right away, but I think you should take a look at that section.
I also personally prefer to split your current hsp in two variables: the direction and speed, where the direction is a value of 1 or -1 and the speed is like hsp, but returns a positive value. And then combine them using speed * direction.
That way, you can calculate the speed without taking direction into account, and vice versa. Which may also solve the conflict you're currently having.

With switch statements, I believe you need to add "break;" in between each Case.
The YoYo documentation here has some good information on switch statements. Now this might not enable your code to run exactly as you're hoping, but it will get you one bug less towards your goal.
The pertinent part of the link states:
"...execution continues after the first case statement with the correct value, until a break statement is encountered....the break is not required, and if there is no break statement the execution simply continues with the code for the next case statement".
So essentially, without a break statement, the code will start at the case block matching the current value, and then run through the code of the all cases below it until it hits a "break;".
Here's what your code would look like with the "break;" added in to the correct spots.
image_speed = 1;
vsp = vsp+grv;
Print(behaviourState);
if (hsp > 0) image_xscale = 3; else if (hsp < 0) image_xscale = -3;
//animation
if (behaviourState == behaviour.idle) sprite_index = tikiAxeWarriorIdle;
if (hsp == 2 || hsp == -2)sprite_index = tikiAxeWarriorWalk;
if (hsp == 4 || hsp == -4) sprite_index = tikiAxeWarriorRush;
if (point_in_circle(playerObject.x,playerObject.y,x,y,200))
{
Print("in circle");
}
Print(hsp);
switch(behaviourState)
{
case behaviour.idle:
{
//stand still
if (alarm[1] <= 0) alarm[1] = room_speed * 4;
if (point_in_circle(playerObject.x,playerObject.y,x,y,200))
{
behaviourState = behaviour.rush;
}
}
break;
case behaviour.wander:
{
if (alarm[0] <= 0) alarm[0] = room_speed * 3;
//move
if (!place_meeting(x + hsp,y,wallObject))
{
x = x + hsp;
}
if (place_meeting(x + hsp,y,wallObject))//checking frame before.
{
hsp = -hsp;
}
//check for behaviour.rush.
if (point_in_circle(playerObject.x,playerObject.y,x,y,200))
{
behaviourState = behaviour.rush;
}
}
break;
case behaviour.rush:
{
//radius in square
if (point_in_circle(playerObject.x,playerObject.y,x,y,200))
{
//direction to face player
if (behaviourState == behaviour.rush && playerObject.x > warriorx) hsp = -4;
else if (behaviourState == behaviour.rush && playerObject.x <= warriorx) hsp = 4;
x = x + hsp;
}
if (!point_in_circle(playerObject.x,playerObject.y,x,y,200))
{
behaviourState = behaviour.idle;
}
}
break;
case behaviour.attack:
{
//attack.
//if player is hit, he dies.
}
break;
}
Hope this helps!
Also, a quick tip!
One thing I noticed with your code is you repeat a couple expressions a few times. For readability sake, I recommend assigning these to a variable if possible. For example:
var in_circle = point_in_circle(playerObject.x,playerObject.y,x,y,200);
if in_circle {
}
if !in_circle {
}

Related

Needing help on printing mode in java

I am given a task to make a method that takes a parameter of an ArrayList of Integer obj and print out the sum, average, and mode.
I can't seem to figure out how to find the mode. It should print out the number if there is only one mode, and it should print out "no single mode" if there is more than one (or none) mode. My method only prints out "no single mode". How can I fix my code to have the mode printed out?
This is what I have for my code:
public static void printStatistics(ArrayList<Integer> arr){
int sum = 0;
for(int i : arr){
sum += i;
}
System.out.println("Sum: "+sum);
System.out.println("Average: "+(double)sum/arr.size());
int temp = 0, counter = 0, max = 0;
for(int j = 0; j < arr.size() - 1; j++){
for(int k = j+1; k < arr.size(); k++){
if(arr.get(j) == arr.get(k)){
counter++;
if(counter > max){
max = counter;
temp = arr.get(j);
}
if(counter == max){
temp = -1;
}
}
}
}
if(temp > 0){
System.out.println("Mode: "+temp);
}
else if(temp < 0){
System.out.println("Mode: no single mode");
}
}
The problem lies here
if(counter > max){
max = counter;
temp = arr.get(j);
}
if(counter == max){
temp = -1;
}
You are assigning the value of counter to max in the first condition so the second if condition i.e., if(counter == max) will always be true, which results in temp having the value -1 which fulfills else if(temp < 0). This is why you are getting Mode: no single mode as the output every time.
Changing the condition should give you the desired output
if(counter < max){
temp = -1;
}

How to fix a bug with 'image_xscale' in GameMaker Studio 2?

I'm making a game in GameMaker Studio 2, and I have a problem. When object turns to left or right, he was puching forward. But he has to be in the same position like the first time.
I tried to use this code:
/// #description vaksciojimas
// You can write your code in this editor
key_right = keyboard_check(vk_right);
key_left = keyboard_check(vk_left);
key_jump = keyboard_check_pressed(vk_space);
var move = key_right - key_left;
hsp = move * walksp;
vsp = vsp + grv;
if (place_meeting(x,y+1,obj_wall)) && (key_jump) {
vsp = -8;
}
//horizontaliai susiduria
if (place_meeting(x+hsp,y,obj_wall)) {
while (!place_meeting(x+sign(hsp),y,obj_wall)) {
x = x + sign(hsp);
}
hsp = 0;
}
x = x + hsp;
//vertikaliai susiduria
if (place_meeting(x,y+vsp,obj_wall)) {
while (!place_meeting(x,y+sign(vsp),obj_wall)) {
y = y + sign(vsp);
}
vsp = 0;
}
y = y + vsp;
//animacijos
if (!place_meeting(x,y+1,obj_wall)) {
sprite_index = sprite_jumping_player;
image_speed = 0;
if (sign(vsp) > 0){
image_index = 1;
}
else {
image_index = 0;
}
}
else {
image_speed = 1;
if (hsp == 0) {
sprite_index = sprite_player;
}
else {
sprite_index = sprite_runing_player;
}
}
if (hsp != 0) { // hsp = horizontal speed
image_xscale = sign(hsp);
}
When I do this, I was wathing this tutorial: https://www.youtube.com/watch?v=fCeyiEcWRAs&t=8s
From the looks of it, I think this has to do with the origin of the sprite.
With the origin, you're deciding where the center of the sprite is. At which point it should rotate/turn around ect.
You need to set the origin in the sprite itself (not the sprite editor, just the sprite image), because by default it's set on top-left. Setting it on middle-center is the usual method. The origin point appears as an "+" symbol.

Getting indexOutOfBoundsException and I don't know why

I have been having this problem for a few hours. I don't know what it is, but I am having a hard time thinking clearly at the moment. This method displays a set of images. The first part of the method is just setting the gridbag constraints, whereas the next part in the if statement is creating jlabels and adding them to an arraylist of jlabels. The exception is being thrown when I try and add mouselisteners to the jlabels after they have been added to the arraylist (this is on line 112, and i have commented this on the code).
public void displayComplexStimulus(BufferedImage[] complexStimulus){
for(int i = 0; i < numberOfElements; i++){
if (i == 0 || i == 1 || i == 2){
c.gridx = i;
c.gridy = 0;
}
else if(i == 3 || i == 4 || i == 5){
c.gridx = i - 3;
c.gridy = 1;
}
else {
c.gridx = i - 6;
c.gridy = 2;
}
if(counter == 1){
if (phase1Trial.getPositionOfCorrectImage()!= i){
phase1IncorrectLabels.add(new JLabel(new ImageIcon(complexStimulus[i])));
phase1IncorrectLabels.get(i).addMouseListener(this); //line 112
add(phase1IncorrectLabels.get(i),c);
}
else if(phase1Trial.getPositionOfCorrectImage() == i){
correctLabel = new JLabel(new ImageIcon(complexStimulus[i]));
add(correctLabel, c);
correctLabel.addMouseListener(this);
}
}
}
}
If i==phase1Trial.getPositionOfCorrectImage() you're not adding an element to phase1IncorrectLabels. So in the next iteration after adding one element to the array it's at position i-1 and not i. You should replace your get(i) by get(phase1IncorrectLabels.size() - 1).

Programming Chess Rook Movement

I am trying to create a board game where all the pieces are able to move the same as a rook in chess. (i.e. Horizontally or vertically as many spaces as they wish)
My board is a simple 2d integer array, with values of 0,1,2 depending on whether the space is empty, has a red piece or a black piece.
My code for the movement so far is shown below, it creates a true or false value if the move is allowed or not:
int[][] board;
public boolean validMove(int fromRow, int fromCol, int toRow, int toCol) {
if (pieceAt(toRow, toCol) != EMPTY) {
return false;
} else if (fromRow - toRow == 0 && fromCol - toCol != 0) {
return true;
} else if (fromCol - toCol == 0 && fromRow - toRow != 0) {
// Trying to add piece collision code
for (int i = fromRow; i < toRow; i++) {
if (pieceAt(toCol, i) != EMPTY)
return false;
}
return true;
} else {
return false;
}
}
My problem is trying to create collision detection, if another piece is in the way it should not be able to move past it, however with my code currently it can. Can anyone help me do this?
Try code below. It's quite naive (and also not tested), but I think it should work just as is. And also I think it illustrates the idea pretty well (see comments). It's in C, but I'm sure you can transform it easily to Java (or whatever language you use).
bool validMove(int fromRow, int fromCol, int toRow, int toCol)
{
int i;
// Attempt to move to the same cell
if (fromRow == toRow && fromCol == toCol)
return false;
// Collision detection
if (fromRow == toRow) {
// Horizontal move
if (fromCol < toCol) {
// Move right
for (i = fromCol + 1; i <= toCol; ++i)
if (pieceAt(fromRow, i) != EMPTY)
return false;
} else {
// Move left
for (i = fromCol - 1; i >= toCol; --i)
if (pieceAt(fromRow, i) != EMPTY)
return false;
}
} else if (fromCol == toCol) {
// Vertical move
if (fromRow < toRow) {
// Move down
for (i = fromRow + 1; i <= toRow; ++i)
if (pieceAt(i, fromCol) != EMPTY)
return false;
} else {
// Move up
for (i = fromRow - 1; i >= toRow; --i)
if (pieceAt(i, fromCol) != EMPTY)
return false;
}
} else {
// Not a valid rook move (neither horizontal nor vertical)
return false;
}
return true;
}
EDIT
You can also optimize your code by reducing conditional statement count, using the method proposed by Toon Krijthe. The main idea is to use "delta" values (dx/dy) for incrementing or decrementing cell indexes. In that case the destination cell should be checked explicitly.
Code:
bool validMove(int fromRow, int fromCol, int toRow, int toCol)
{
int i;
// Attempt to move to the same cell
if (fromRow == toRow && fromCol == toCol)
return false;
// Collision detection
if (fromRow == toRow) { // Horizontal move
int dx = (fromCol < toCol) ? 1 : -1;
for (i = fromCol + dx; i != toCol; i += dx)
if (pieceAt(fromRow, i) != EMPTY)
return false;
} else if (fromCol == toCol) { // Vertical move
int dy = (fromRow < toRow) ? 1 : -1;
for (i = fromRow + dy; i != toRow; i += dy)
if (pieceAt(i, fromCol) != EMPTY)
return false;
} else { // Not a valid rook move
return false;
}
// Return true if destination cell is free
return pieceAt(toRow, toCell) == EMPTY;
}
you could have a 2D char array with each cell representing the position on the board. there you can have a char for the three states of the position (white/red/empty).
The other way i can think of is before each move to check from the startPosition till the endPosition positions their states. but performance wise i think the some kind of array will be much better
You can simply "walk" each field in order to check if it is empty.
For example:
int[][] board;
public boolean validMove(int fromRow, int fromCol, int toRow, int toCol)
{
if (pieceAt(toRow, toCol) != EMPTY) return false;
else if (fromRow == toRow)
{
// horizontal move
if (fromCol == toCol) return false; // same position
int dx, x;
if (fromCol < toCol)
dx = 1;
else
dx = -1;
for (x = fromCol + dx; x != toCol; x += dx)
{
if (pieceAt(toRow, x) != EMPTY) return false; // occupied
}
}
else if (fromCol == toCol)
{
// vertical move
int dy, y;
if (fromRow < toRow)
dy = 1;
else
dy = -1;
for (y = fromRow + dy; y != toRow; y += dy)
{
if (pieceAt(y, toCol) != EMPTY) return false; // occupied
return true; // free path
}
}
else return false; // no horizontal or vertical move
}

Negamax not working beyond depth 1

I am making a chess engine in C++ and with this algorithm, I get expected play with max depth set to 1. Beyond that however, it ignores pieces that are in danger and seems to even willingly put itself in danger.
Here is my code:
int negamax(int depth, int alpha, int beta)
{
int max = -INFINITY;
MoveList legalMoves;
MoveGeneration::generateCaptureMoves(&legalMoves);
MoveGeneration::generateQuietMoves(&legalMoves);
// No legal moves
if(legalMoves.count == 0)
{
if(Position::isCheck())
{
// Checkmate
if(Position::activeColor == WHITE)
return VAL_VICTORY;
else
return -VAL_VICTORY;
}
else
{
// Stalemate
return 0;
}
}
// Go through legal moves
for(int i = 0; i < legalMoves.count; i++)
{
// Get move score
Position::doMove(legalMoves[i]);
int score;
if(depth == 0)
score = quiescence(MAX_QUIESCENCE_DEPTH, alpha, beta);
else
score = -negamax(depth - 1, alpha, beta);
Position::undoMove();
// Pruning
if(Position::activeColor == WHITE && score > beta) break;
if(Position::activeColor == BLACK && score < alpha) break;
if(Position::activeColor == WHITE && score > alpha) alpha = score;
if(Position::activeColor == BLACK && score < beta) beta = score;
// Best so far?
if(score > max)
{
max = score;
if(depth == MAX_DEPTH)
bestMove = legalMoves[i];
}
}
return max;
}
Try:
score = -negamax(depth - 1, -beta, -alpha);