Game Maker - Check Collision With Subimages - mouse

I have an obj_roulette, which contains 4 subimages, with value 2-5 and image_number 0-3. The value result from roulette stored as var global.roulette.
Then, I make many obj_meteorite, which contains 4 subimages too, spawn from above with random x value and random image_number. Player can shoot them with left-mouse click.
This is what I want:
If image_number obj_roulette is 0, and player shoot obj_meteorite with image_number 0, score +10.
If image_number obj_roulette is 0, and player shoot obj_meteorite with image_number 1, score -10.
I don't know how to check collision between mouse_x/mouse_y and object image_number, and how to match obj_roulette image_number and obj_meteorite image_number.
Is it using collision checking? If it yes, then maybe the examples in these links can help:
link 1
link 2
Please explain your answer. Thanks.

I assume this is the kind of game where you click with your mouse and hit exactly where the mouse was clicked. And as I understand it from your question. If the mouse is clicked and obj_roulette's image_index is the same as obj_meteorite, you want to add 10 to the score. If not, you want to subtract 10 from the score. And you need help converting your pseudo-code into gml.
// Check if obj_meteorite was clicked
if (mouse_check_button_released(mb_left) && position_meeting(mouse_x, mouse_y, obj_meteorite))
{
// Check wheter or not obj_meteorite's and obj_roulette's image_index is the same
if (obj_meteorite.image_index == obj_roulette.image_index)
{
// Add 10 to the score
score += 10;
}
else
{
// Subtract 10 from the score
score -= 10;
}
}
If this is not what you want, I suggest editing your question to make it more clear. Preferably explain shortly what your game is actually about.

Related

How to get current frame from Animated Tile/Tilemap.animationFrameRate in Unity

I am using tilemaps and animated tiles from the 2dExtras in unity.
My tiles have 6 frames, at speed=2f, and my tilemap frame rate is 2.
New tiles placed always start on frame 1 and then immediately jump to the current frame of the other tiles already placed, the tilemap is keeping every tile at the same pace, which is working as I want.
However I would like the newly placed tiles to start at the frame the others are currently on,(instead of placing a tile that jumps from frame 1 to frame 4) I would like the new tile to start on frame 4
I've found how to pick the frame I want to start on, however I am having trouble retrieving which frame the animation is currently on, so I was wondering how exactly can I access the current frame of animation of a given tilemap ( Or a given tile, I can create a dummy tile and just read the info out of it, how can I get the current frame of an animated tile? )
The animated tilemaps feature seems to lack the feature to retrieve this information, also when I try tilemap.getsprite it always returns the first frame of the sequence(does not return the sprite currently displayed), and there doesn't seem to be any method to poll info from tilemap.animationFrameRate.
I thought another method would be to set a clock and sync it to the rate of the animation but since I can't get the exact framerate duration the clock eventually goes out of sync.
Any help would be appreciated!
I found a way to solve this question. But it's not 100% insurance.
First of all, I used SuperTile2Unity. That doesn't seem to be the point.
private void LateUpdate()
{
// I use this variable to monitor the run time of the game
this.totalTime += Time.deltaTime;
}
private void func()
{
// ...
TileBase[] currentTiles = tilemap.GetTilesBlock(new BoundsInt(new Vector3Int(0, 0, 0), new Vector3Int(x, y, 1)));
Dictionary<string, Sprite> tempTiles = new Dictionary<string, Sprite>();
//I use SuperTiled2Unity. But it doesn't matter, the point is to find animated tile
foreach (SuperTiled2Unity.SuperTile tile in currentTiles)
{
if (tile == null)
{
continue;
}
if (tile.m_AnimationSprites.Length > 1 && !tempTiles.ContainsKey(tile.name))
{
// find animated tile current frame
// You can easily find that the way SuperTile2Unity is used to process animation is to generate a sprite array based on the time of each frame set by Tiled animation and the value of AnimationFrameRate parameter.
// The length of array is always n times of AnimationFrameRate. You can debug to find this.
tempTiles.Add(tile.name, tile.m_AnimationSprites[GetProbablyFrameIndex(tile.m_AnimationSprites.Length)]);
}
}
//...
}
private int GetProbablyFrameIndex(int totalFrame)
{
//According to the total running time and the total length of tile animation and AnimationFrameRate, the approximate frame index can be deduced.
int overFrameTime = (int)(totalTime * animationFrameRate);
return overFrameTime % totalFrame;
}
I have done some tests. At least in 30 minutes, there will be no deviation in animations, but there may be a critical value. If the critical time is exceeded, there may be errors. It depends on the size of AnimationFrameRate and the accumulation mode of totalTime. After all, we don't know when and how the unity deals with animatedTile.
You could try using implementation presented in [1] which looks as follows:
MyAnimator.GetCurrentAnimatorClipInfo(0)[0].clip.length * (MyAnimator.GetCurrentAnimatorStateInfo(0).normalizedTime % 1) * MyAnimator.GetCurrentAnimatorClipInfo(0)[0].clip.frameRate;
[1] https://gamedev.stackexchange.com/questions/165289/how-to-fetch-a-frame-number-from-animation-clip

Sprite Kit: Waiting to call a function until condition X is met

I'm working on a game (top-down shooter) and have run into a bit of a snag. Up to this point, I've spawned enemies with functions that just work with delays:
Wave One Function - Delay 3, spawn enemies //
Wave Two Function - Delay 6, spawn enemies
I do this because I haven't found a way to wait for all actions in a given function to complete before calling the next - as things stand, functionWaveOne calls functionWaveTwo, which calls functionWaveThree (etc).
This has worked until now. What's changed is I want two enemies to remain on-screen and until they're dead, I don't want the next wave to come. My initial solution to this was to have a running count of how many enemies died in wave four:
Detect collision -> Apply Damage -> Check if dead -> If yes, deadWaveFourEnemies++
Here's my problem: I have a primary weapon that's two parallel lasers, so they have potential to collide with an enemy at exactly the same time. This results in false positives, making the dead enemy count go higher than it should. I even tried adding an "am I alive" function to the enemy class, but to no avail.
Have any of you got a recommendation on how to either call functions in a better way or to get around these false positives? Thanks!
In case it helps:
if([enemySprite.name isEqual: #"shooter"]){
enemyShooter *enemyClass = (enemyShooter *)enemySprite;
itDied = [enemyClass doDamageWithAmount:secondaryWeaponDamage];
scoreIncrease = [enemyClass getScoreIncrease];
stillAlive = [enemyClass amIAlive];
}
[weaponSprite removeFromParent];
if(itDied){
[self increasePlayerScoreBy:scoreIncrease];
if(inWaveFour == 1 && stillAlive == 1){
waveFourKilled++;
NSLog(#"Seconday / Number killed: %i", waveFourKilled);
}
}

Force the SKAction.move to be completed - swift

I'm creating a Snake Game using swift and the library SpriteKit.
To control the direction of the snake, the user has to swipe on the screen. To check which was the direction of the swipe, I use UISwipeGestureRecognizer .
Now, I have to pass the information (direction of the swipe) from the GameViewController.swift file to the GameScene.swift file. To to that, I declare a 4 items array called movesConoller:
movesController[false, false, false, false]
If the user swipes up, the first element of the array turns true, if he scrolls down, the second element turns true and the first one false... etc. etc.
Now, in the GameScene.swift file I have to say what the Snake has to do if the player moves up.
For this reason, I use 2 more variables:
var playerX:CGFloat = 0
var playerY:CGFloat = 0
and then I created this code
if movesController[0] == true {
playerY += 56
}
if movesController[1] == true {
playerY -= 56
}
if movesController[2] == true {
playerX += 56
}
if movesController[3] == true {
playerX -= 56
}
Now I create the movement
let startPoint = CGPoint(x: player.position.x, y: player.position.y )
let endPoint = CGPoint(x: playerX + player.position.x, y: playerY + player.position.y)
let moveIt = SKAction.move(to: endPoint, duration:0.1)
player.run(moveIt)
So, every time the program is executed, the playerX and playerYvariables become equal to 0. Then, based on the direction of the swipe, they are added or subtracted 56.
To move the snake I just say to the head to move from his startPointto his endPoint in 0.1 seconds.
Another thing I added is the tail. To create it I use two arrays, snakeX and snakeY.
To these two empty arrays (of type CGFloat) I add the previous position of the snake and then, if the score remains the same, the last element of each array is deleted. Else, the last element isn't deleted and remains in his array.
Whit this method, I make the tail growing when the snake eats an apple.
BUT the head moves 56 in 0.1seconds. This means that he makes a movement of 8 pixels at each execution. Because of that, I have to add to snakeX and snakeY the X and Y values every 7 execution of the program.
This is the problem. If the player swipes on the screen just after 7 execution of the program, the movement of the tail will be perfect. But if the player swipes before the 7 execution, there will be a problem. Let's suppose the snake is moving right and the player swipes up when the program is at its 4th execution.
//snakeX and snakeY values before the swipe
snakeX[168, 112, 56, 0] //all this numbers are multiple of 56
snakeY[224, 224, 224, 224] //the snake is moving right, the Y value doesn't change.
//snakeX and snakeY values after the swipe
snakeX[168 ,112 ,56 , 0]
snakeY[248, 224, 224, 224] //look at the first element
248 is not a multiple of 56. The snake moved Up at the 4th execution, after 3 execution his position will be added to the arrays. But in 3 execution he had moved of 24pixel.
Because of that, I'll get this bug
as you can see, the tail doesn't make a perfect corner.
The snake head doesn't complete his movement of 56 pixels. When I swipe, it leaves his movement and starts another one. Is there a way I can tell the head to always complete his movement before doing another one?
You're sort of trying to use a pixel-based approach to what's basically a grid-based game, but maybe something like this will work...
Make a variable controlling whether movement is allowed:
var moveAllowed = true
Guard your movement code with that variable:
if moveAllowed {
if movesController[0] == true {
playerY += 56
}
...
}
When you schedule the movement action, toggle moveAllowed, and add a completion handler to toggle it back after the action finishes:
...
moveAllowed = false
let moveIt = SKAction.move(to: endPoint, duration:0.1)
player.run(moveIt) { self.moveAllowed = true }
(The self.moveAllowed might be just moveAllowed depending on how you have things structured, but I can't tell from your fragments.)
Edit: I just realized that maybe you're setting snakeX and snakeY based on player.position. It's not really clear. In any case, then you'd use the same idea but copying from player.position only when moveAllowed is true. Basically that variable is telling you if the action is still running or not.

Multiple for loops executing at the same time? Swift

Basically I am creating a game that spawns a ball after a certain amount of time has passed. However, as time goes on and the player score increases, balls that are more difficult to kill will spawn.
Prior to this, all the spawn methods I have set in place function as I would like them to, but they were contained in a switch statement that calculated the probability a ball would spawn.
//pseudo:
let spawnProbability = arc4random_uniform(100)
switch spawnProbability {
case 0…60: //60% spawn chance
//spawn small ball
case 61…90: //~30% spawn chance
//spawn medium ball
case 91…100: //~9% chance
//spawn large ball
default:
break
}
This worked as a plan B, but I'd like to implement what I originally had designed which was the method described above.
My thought process to accomplish what I originally had in mind is:
for loop that spawns small balls until the score counter reached a certain amount
after the score counter reached, say, 100pts, another for loop would be executed in addition to the first loop. So, medium and small balls spawn after a delay.
after the score counter reached another mark, large balls would begin to spawn on top of medium balls and small balls.
Here's what I mean: (pseudocode again)
for(var i = 0; i < 10; i++) {
//delay
//spawn small ball
}
if score >= 100 {
for (var x = 0; x < 10; x++) {
//delay
//spawn medium ball
}
}
// . . . and so on
I've put alot of thought into how I could accomplish what I want but I can't come up with a better approach to this problem. I understand why this isn't working and why multiple for loops can't be executed with delay in the same class at the same time (or maybe they can and I'm just going about it in the wrong way) but I'm stuck in the sense that I don't know a better way to implement the spawn technique that I want.. Any help is greatly appreciated!!

Pygame check for collisions with the top most foreground rect

I have 10 sprites which I objects of the main sprite I wrote with different images and starting positions etc. But they all behave the same way. They are sub sprites of the main sprite.
I want to be able to hold mouse click on one's rect and move it round the screen which works perfectly fine. But the problem is they all have the same controls click and drag to move them. So if I am clicking on one of the sprite rects and I drag it over another one it picks it up as well. And I don't want that to happen.
Is there a way to only check for collisions with the top most foreground rect or if someone could explain a way of doing this that would achieve similar results. I have had a look at the rect documentation but I can't find a solution.
def update(self,):
self.move(self.rect)
def move(self,rect):
if pygame.mouse.get_pressed() == (1, 0, 0) and the_rect.collidepoint(pygame.mouse.get_pos()):
self.state = 1
elif pygame.mouse.get_pressed() == (0, 0, 0) and the_rect.collidepoint(pygame.mouse.get_pos()):
self.state = 0
if self.state == 0:
the_rect.centerx = the_rect.centerx
the_rect.centery = the_rect.centery
elif self.state == 1:
(the_rect.centerx, the_rect.centery) = pygame.mouse.get_pos()
Rather than using the pygame.mouse.get_pressed() function, use the event queue and check for a pygame.MOUSEBUTTONDOWN event. It will only get fired once when the button is first pressed.