Phaser multitouch (on laptop trackpad) - multi-touch

I'm confused by how to skip the following condition when 1) holding down, and then 2) another tap on the screen to release the aim. I think the secondary tap becomes the activePointer so I'm quite puzzled.
var holding_down = game.input.activePointer.isDown;
if (holding_down && game.input.pointer2.isUp) { cue.aiming = true; }
UPDATE: Please note that for the solution accepted, I had to differentiate between desktop and mobile usage. pointer1 & pointer2 work fine on mobile, but on desktop I had to do the following.
if (desktop) {
var holding_down = game.input.activePointer.leftButton.isDown;
var second_tap = spacebar.isDown;
} else {
var holding_down = game.input.pointer1.isDown;
var second_tap = game.input.pointer2.isDown;
}
Also note you need to declare the desktop var after intantating the game object. I then set the var in the preload() function: desktop = game.device.desktop;, otherwise it was giving the wrong value. Thanks!

You're correct in that the secondary tap becomes the activePointer. Per the documentation, activePointer is "[t]he most recently active Pointer object."
Therefore, you'll want to make your checks against game.input.pointer1 and game.input.pointer2 instead.
So replace activePointer in your code with pointer1 and that might get you closer to what you were looking for.

Related

Using if let... don't understand where data is coming from

I am following the Stanford CS193 SwiftUI iOS Development Course on YouTube and I am having a very difficult time comprehending how a certain piece of the code is working.
import Foundation
struct MemoryGame<CardContent> where CardContent: Equatable {
private(set) var cards: Array<Card>
**private var indexOfTheOneAndOnlyFaceUpCard: Int?**
mutating func choose(_ card: Card) {
if let chosenIndex = cards.firstIndex(where: { $0.id == card.id }),
!cards[chosenIndex].isFaceUp,
!cards[chosenIndex].isMatched
{
**if let potentialMatchIndex = indexOfTheOneAndOnlyFaceUpCard** {
**if cards[chosenIndex].content == cards[potentialMatchIndex].content** {
cards[chosenIndex].isMatched = true
cards[potentialMatchIndex].isMatched = true
}
indexOfTheOneAndOnlyFaceUpCard = nil
} else {
for index in cards.indices {
cards[index].isFaceUp = false
}
indexOfTheOneAndOnlyFaceUpCard = chosenIndex
}
cards[chosenIndex].isFaceUp.toggle()
}
print("\(cards)")
}
I have put asterisks around the lines I'm focusing on. I do not understand how cards[chosenIndex].content is being compared to cards[potentialMatchIndex].content, which stems from me not understanding how the if let potentialMatchIndex = indexOfTheOneAndOnlyFaceUpCard line is working. I believe I have a general understanding of optionals and how this code is running but I truly don't understand where the data/value is coming from for indexOfTheOneAndOnlyFaceUpCard.
Here is the video for reference. He begins talking about these lines of code at 1:12:40. Thank you for any help in advance!
In the memory game, a turn proceeds in two stages.
First, one card is turned face up.
Then, the user chooses another card. At that point, one of two things can happen: the new card is a match with the face up card or it isn't. But either way, that turn is over and all cards are now face down.
Thus, when the user chooses a card, if there is a face up card, we are in the middle of the turn, and we arrange things so there is no face up card; the turn is over.
But if there is no face up card, we are at the start of the turn, and we just turn the chosen card face up and wait for the second part of the turn.
So as far as the variable that tracks the face up card is concerned, the logic works like this:
var faceupCard : Card? = nil
if let card = faceupCard {
faceupCard = nil
} else {
faceupCard = chosenCard
}
So that's very clear, when pared down in that way: if the card is face up, make it not be face up; if it isn't face up, make it be face up.
The reason you are confused, in my opinion, is that this is a really bad way to write the code. The face-up-ness of a card is being used as a signal for whether we are in the middle of the turn or at the start of the turn. That's terrible programming. The way to signal what stage we are at is to have an enum whose job is to say what stage we are at! When you are programming you should say what you mean, not use a value in two different ways as is being done here (both track what card is face up and signal what stage of the turn we're on).
Another reason you might be confused is that everything has to be done in terms of indexes, because Card is a value type (struct). We cannot cycle through Cards, or point directly to the face up Card, or even ask a Card whether it is face up; we have to cycle thru indexes in our array of Cards, and track the index of the face up Card.

Stop SKAudioNode from Playing

I am trying to play footstep sounds only when the player is moving. I added a check to see if the player is already moving to make sure it would not play audio on top of each other.
For some reason footstepSound.run(SKAction.stop()) is not working which is causing a bunch of tracks to play over each other which sounds very bad.
Any ideas?
let footstepSound = SKAudioNode(fileNamed: "Footsteps.mp3")
if (degree != 50) {
if (isFootstepping) { return }
isFootstepping = true
self.addChild(footstepSound)
}
else if (isFootstepping && degree == 50) {
isFootstepping = false
footstepSound.run(SKAction.stop())
footstepSound.removeFromParent()
}
Solved! The issue is that the footstepSound variable was not globally declared. This was causing a new variable to be created on each function call, therefore when .removeFromParent was called it was never referencing the old variable.

Part of my PlayerExpChangeEvent is being overridden by vanilla

I'm making a spigot plugin (version 1.8.8) that has an function that I know works because it fires flawlessly through my command. However, when I call it at the end of a PlayerExpChangeEvent, it seems like vanilla leveling overrides the bar, making it go up way more that it is supposed to. Running the command/function after this happens makes the bar go back to how it is supposed to be. I've tried setting my event's priority to highest (and when that didn't work, to lowest) but no matter what my function appears to be completely ignored when called inside the event.
Here is some code:
#EventHandler(priority=EventPriority.HIGHEST)
public void onXpGain(PlayerExpChangeEvent event)
{
// Load custom levels from config
ArrayList<String> levelList = new ArrayList<String>(plugin.getConfig().getStringList("levels"));
if (!((String)levelList.get(0)).equals("none"))
{
Player player = event.getPlayer();
Iterator<String> var4 = levelList.iterator();
while (var4.hasNext())
{
String s = (String)var4.next();
String[] splits = s.split(" ");
int levelCompare = Integer.parseInt(splits[0]);
int playerLvl = player.getLevel();
// Detect if on correct tier, else continue iteration
if (playerLvl == levelCompare - 1)
{
// Calculate the player's new XP amount
int totalXp = player.getTotalExperience() + event.getAmount();
player.setTotalExperience(totalXp);
updateBar(event.getPlayer()); // <-- THIS IS THE FUNCTION
return;
}
}
// At max level
player.setTotalExperience(player.getTotalExperience() + event.getAmount());
player.setLevel(getHighestLevel(levelList));
player.setExp(1.0f);
}
}
And here is the function itself. Keep in mind that it works fine when called through a command and not an event. It's purpose is to use the player's total XP to set the level and bar. Neither set correctly in the event; it instead embraces vanilla leveling.
public static void updateBar(Player player) {
ArrayList<String> levelList = new ArrayList<String>(plugin.getConfig().getStringList("levels"));
int totalXp = player.getTotalExperience();
player.setLevel(getHighestLevelForXp(totalXp, levelList));
if (player.getLevel() < getHighestLevel(levelList)) {
int lvlDiff = getTotalXpToLevel(player.getLevel() + 1,levelList) - getTotalXpToLevel(player.getLevel(),levelList);
int xpDiff = totalXp - getTotalXpToLevel(player.getLevel(),levelList);
player.setExp((float)xpDiff/lvlDiff);
} else {
player.setLevel(getHighestLevel(levelList));
player.setExp(0.0f);
}
return;
}
The command where the function works correctly is a bare-bones call to the function and doesn't need a mention here. Does anyone know how to get my event to override vanilla xp gain? The update works through the command, just not natural xp gain. It is already confirmed that the event DOES fire, as the rest of the event changes the internal xp amount, but the visual effects are overridden by vanilla. Can anyone help? Thanks.
Only setting the Player's EXP won't be enough for your desired behaviour. The Vanilla behaviour will still complete, as you're not changing how the event will add EXP to the player.
Currently, your event is working like this:
And PlayerExpGainEvent isn't cancellable, so you cannot undo it's addition of EXP.
What you can do instead is to set the EXP the event will add to 0, therefore not changing the player's EXP after your interception.
event.setAmount(0); //Cancelling the EXP addition
I would recommend to set your event to a high priority, so that other events that depend on Experience gain won't trigger when you set the amount gained to 0.

A way to check a BufferedImage repaint()?

I'm working in Eclipse and I want to know if I can make an if statement that checks to see if the BufferedImage has been painted/drawn onto the frame. For some reason, it's not painting the correct image because clickable regions are appearing on that picture when they are not supposed to.
For example, when I click the region to go from 4>5 everything is good. When I click from 5 to go to 4 I end up at 6 because the 'regions' from 4 are appearing in 5 (The image should always be painted before the clickable regions are shown) before it's even being painted. I want to restrict this to check if the image has been painted onto the frame first.
I really don't want to use anything else besides what I have right now (so no new classes being implemented to do this task), I really just want a simple yet effective way to resolve this. Here is what I'm talking about:
...
MouseAdapter mouseHandler = new MouseAdapter()
{
public void mouseClicked(MouseEvent e)
{
repaint();
if(n==0)
{
if(e.getX()>=459 && e.getX()<491 && e.getY()>=111 && e.getY()<133
{
n = 4;
}
return;
}
if(n==5)
{
if(...)
{
n = 4;
}
return();
}
if(n==6)
{
if(...)
{
n = 5;
}
if(...)
{
n = 0;
}
if(...)
{
n = 6;
}
return();
}
}
...
I think you might need to give a little more information. The problem might lie in how you repaint, not whether it was painted.
If you are running another thread as your main program, you might instead want to send the mouse events synchronously to that so that the main thread can process the mouse click and then repaint.
Another solution might be to override the repaint method and paint the buffered images there, but you may have done that.
Also, a little off topic, I noticed that you used for loops to determine if the mouse was clicked in a specific area.
You could shorten the code:
for(int i=459; i<491; i++){
if(e.getX()==i){
for(int j=111; j<133; j++){
if(e.getY()==j){
//action taken
}
}
}
}
To:
if(e.getX()>=459 && e.getX()<491 && e.getY()>=111 && e.getY()<133{
//action taken
}
This would take up less space in your code and less time checking every pixel.
Back to your question.
I dont know of a function to tell if a buffered image has been painted. The ploblem that you are having though might of might not be in the code provided. Posting the rest of your code would be beneficial.
Okay I found the solution, I forgot to come back to this question and let you know. The problem was that the mouse was double clicking for some reason. You could almost say it was 'recursive'. I decided to move the mouseListener from inside the paintComponent to outside of it, and surely enough that fixed it.

Code will not work without CCLOG in Cocos2D

I'm currently working on a game that will randomly generate a dungeon for the player. This is made up of a whole heap of different methods being called from different files. I seem to have the code working to be able to calculate how much space is available on the other side of the door.
There is a catch however; the code only seems to work when I am outputting to the console using CCLOG. I wrote a good chunk of the code without testing and decided to then work through it in steps to see how the code worked (I figured I would just get it to run without bugs and next I will check my values).
After I had established that the code was successfully checking available space in each direction, while listing the locations it had checked, I decided that I would like to remove the CCLOG output. Unfortunately though, doing this caused the code to stop working.
//First check "forward"
bottom = CGPointMake(startLocation.x + forwardDirectionModifier.x, startLocation.y + forwardDirectionModifier.y);
top = bottom;
do
{
currentLocation = CGPointMake(top.x + forwardDirectionModifier.x, top.y + forwardDirectionModifier.y);
tileType = [tileCache getTileType:currentLocation];
if (tileType == TileTypeFiller)
{
top = currentLocation;
CCLOG(#"Top location is %#", NSStringFromCGPoint(top));
}
} while (tileType == TileTypeFiller || top.y != 63);
This is a small snippet from the code; It is the section of code I think is most likely the issue. Essentially the problem is that if I comment out or delete the line CCLOG(#"Top location is %#", NSStringFromCGPoint(top)); it will stop working.
Here are some more details:
I have several little chunks of code like this, as long as I CCLOG in each one, it will be able to move to the next. If i were to comment out any of them, it would stop progress to the next chunk.
I can change the CCLOG to output anything and it will still work, it just has to be there.
I have tried cleaning the project.
There are more CCLOG's that aren't used inside any loops and they can be removed without consequence.
Does anyone have a solution to this? As far as I can tell, whether I do or do not output something to the console,it shouldn't have an effect on whether or not the code will execute.
Thanks in advance.
EDIT: At Ben Trengrove's suggestion, I am adding in further examples of where the CCLOG is being used.
This code immediately follows the previously listed code.
//Check left relative to door
farLeft = CGPointMake(startLocation.x + leftDirectionModifier.x, startLocation.y + leftDirectionModifier.y);
do
{
currentLocation = CGPointMake(farLeft.x + leftDirectionModifier.x, farLeft.y + leftDirectionModifier.y);
tileType = [tileCache getTileType:currentLocation];
if (tileType == TileTypeFiller)
{
farLeft = currentLocation;
CCLOG(#"Far Left location is %#", NSStringFromCGPoint(farLeft));
}
} while (tileType == TileTypeFiller || farLeft.x !=0);
//Check forwards from far left
top2 = farLeft;
do
{
currentLocation = CGPointMake(top2.x + forwardDirectionModifier.x, top2.y + forwardDirectionModifier.y);
tileType = [tileCache getTileType:currentLocation];
if (tileType == TileTypeFiller)
{
top2 = currentLocation;
CCLOG(#"Top2 location is %#", NSStringFromCGPoint(top2));
}
} while ((tileType == TileTypeFiller)|| (top2.y != 63));
What does your tile cache return as a tileType when there is no tile at currentLocation ? TileTypeFiller by any chance ? if yes, you have an almost certain never ending loop there, under the right circumstances..
EDIT :
please add a line after the loop
CCLOG(#"");
and place a breakpoint on that line. See if the program stops there.
So the problem has now been solved, it seems that in part there was a fault in my logic.
Here is what I've done that now works:
do
{
currentLocation = CGPointMake(top.x + forwardDirectionModifier.x, top.y + forwardDirectionModifier.y);
tileType = [tileCache getTileType:currentLocation];
if (tileType == TileTypeFiller)
{
top = currentLocation;
}
locationCount++;
} while ((tileType == TileTypeFiller) && (locationCount != 15));
CCLOG(#"Top location is %#", NSStringFromCGPoint(top));
The change is that I've switched to AND as opposed to OR. I also changed the second part of the while as I realised that due to my max room size, I never needed to check more than 15 locations in any given direction.
Thanks a lot to everyone that has helped. I'm still not entirely sure though how the use of CCLOG was somehow able to help get past a fault in logic but at least it is working now.