Modelling card game zones using arrays: how to check what zone an object is in? - arrays

I've been building a little card game with a friend. We used ds_lists for building the hand, deck, play zone and discard, moving the card objects back and forth between them. One part we've struggled with was checking what zone a card is in. For example: You can see both your hand and the discard at all times but only cards in hand should be playable, i.e. move when dragged into play.
Since the objects don't know what list they're in, we've been manually updating a list of booleans inDeck, inHand, inPlay etc. then nesting the tap event logic in an if block.
Create Event:
var inDeck = false;
var inHand = false;
var inPlay = false;
var inDiscard = false
if (ds_list_find_index(Hand, card) >= 0)
{
inHand = true
}
else if (ds_list_find_index(Play, this_card) >= 0)
{
inPlay = true
}
Repeat for each zone
Tap event:
if (inHand)
{
playCard();
}
if (inPlay)
{
useCard();
}
Is this the best we can do or is there a different approach we could/should take?

Related

Is there a way to prevent node glitching shaking during collisions?

The function (code below) simply moves a character node (contact.nodeA) back after it has penetrated a collision node (contact.nodeB) that it's not allowed to pass through.
In a scenario where a user is trying to move the character node (contact.nodeA) while it's stuck in a corner, etc. the scene/character repeatedly shakes/glitches while its position is repeatedly being moved back away from contact.nodeB at 60hz.
Is it possible with SCNPhysicsContact in SceneKit to prevent contact.nodeA from moving forward into contact.nodeB in the first place? (ie., why does "contact.penetrationDistance" occur at all? Why can't we just be alerted when a physics contact has begun w/o contact.nodeA actually moving forward into contact.nodeB? We are, after all, catching the contact on "didBegin" yet there's already penetration that we have to readjust.)
(When a user is trying to move the character node while it's "stuck" in a corner, or between two rocks, etc.)
Or, is there another solution to prevent the repeated shaking of contact.nodeA as its position is repeatedly being readjusted to compensate for its penetration into contact.nodeB? Transform instead of position change?
I've already tried setting momentum to 0.0 and removing all animations, etc. However, since contact is still occurring with contact.nodeB, contact.nodeA cannot be moved away since its momentum is still being stopped during contact with nodeB.
Any help would be greatly appreciated.
private func characterNode(_ characterNode: SCNNode, hitWall wall: SCNNode, withContact contact: SCNPhysicsContact) {
if characterNode.name != "collider" { return }
if maxPenetrationDistance > contact.penetrationDistance { return }
var characterPosition = float3(characterNode.parent!.position)
var positionOffset = float3(contact.contactNormal) * Float(contact.penetrationDistance)
maxPenetrationDistance = contact.penetrationDistance
characterPosition.y += positionOffset.y
replacementPositions[characterNode.parent!] = SCNVector3(characterPosition)
}
extension GameViewController : SCNPhysicsContactDelegate {
func physicsWorld(_ world: SCNPhysicsWorld, didBegin contact: SCNPhysicsContact) {
if gameState != .playing { return }
contact.match(BitmaskWall) {
(matching, other) in
if other.name == "collider" {
self.characterNode(other, hitWall: matching, withContact: contact)
}
}
}

How to detect a collision between two objects in an Array (Sprite Kit)

I have an array of Bullets and Enemies objects. I want the game to check each element in the arrays to see whether there was a collision between the two or not. If there was any collision, then damage that enemy and delete the bullet. Right now every time a bullet hits an enemy it decreases the health of ALL the enemies in the array, thus killing them all at once. How can I make it so that it only decreases the health of the enemy which I'm shooting at?
//This function handles our collision detection
func didBegin(_ contact: SKPhysicsContact) {
//print("COLLISIONS!")
// If contact is with another object
if contact.bodyA.categoryBitMask == pickupCategory && contact.bodyB.categoryBitMask == playerCategory
{
displayText(text: "Picked up Pistol")
player.setHasPistol(gotPistol: true)
pistol.removeFromParent()
}
//Bullet hits an object
if contact.bodyA.categoryBitMask == objectCategory && contact.bodyB.categoryBitMask == bulletCategory
{
bulletCleanup(killNow: true)
//print("BULLET HAS HIT THE PIPE!")
}
//Do collisions between bullets and enemies...
for bullet in bulletList
{
for enemy in enemyList
{
//if the enemy was hit have them take damage.
if contact.bodyA.categoryBitMask == enemyCategory && contact.bodyB.categoryBitMask == bulletCategory
{
//deletes the bullet after hitting an object
bulletCleanup(killNow: true)
//apply damage
enemy.setHitPoints(setPoints: enemy.getHitPoints() - pistol.getDamage())
// print("BULLET HAS HIT THE enemy!!!!")
}
}
}
}
didBegin(contact:) is called for a single collision between one specific object and another specific object. (actually between physics bodies...)
Your problem is occurring because you are iterating over every bullet and every enemy for a single collision and at each iteration you are testing if the collision was between a bullet and an enemy, which it was, so you are applying the damage to every enemy.
All you need to do is to extract the specific enemy from the SKPhysicsContact object and apply damage to that particular enemy.
Also, I suspect you are missing some contacts because for your contact tests, you are not checking if the bodyA and bodyB physics bodies are reversed i.e.
if contact.bodyA.categoryBitMask == pickupCategory && contact.bodyB.categoryBitMask == playerCategory
bodyA might be player and bodyB might be pickup, so you need to test for this also.
Here is an alternative didBegincontact() you might find helpful (It's Swift2, but that shouldn't be a big problem):
func didBeginContact(contact: SKPhysicsContact) {
let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
switch contactMask {
case pickupCategory | playerCategory
displayText(text: "Picked up Pistol")
player.setHasPistol(gotPistol: true)
let pistol = (contact.bodyA.categoryBitMask == pickupCategory) ? contact.bodyA.node! : contact.bodyB.node!
pistol.removeFromParent()
case objectCategory | bulletCategory
bulletCleanup(killNow: true)
//print("BULLET HAS HIT THE PIPE!")
case enemyCategory| bulletCategory
//deletes the bullet after hitting an object
bulletCleanup(killNow: true)
//apply damage
let enemy = (contact.bodyA.categoryBitMask == enemyCategory) ? contact.bodyA.node! : contact.bodyB.node!
enemy.setHitPoints(setPoints: enemy.getHitPoints() - pistol.getDamage())
default :
//Some other contact has occurred
print("Some other contact")
}
}
I'm not sure how your bulletCleanup() function works - you don't appear to be sending it a specific bullet object so I don't know how it knows which bullet to process. The specific bullet involved in the contact can be obtained in didBegincContact: as follows:
let bullet = (contact.bodyA.categoryBitMask == bulletCategory) ? contact.bodyA.node! : contact.bodyB.node!
which is a short form of saying "is nodeA's categoryBitMask a bulletCategory? If it is, then set bullet equal to nodeA, otherwise set bullet to nodeB.
Note that if your categories are more complicated (i.e. objects can belong to multiple categories), this won't work as the simple AND tests (enemyCategory| bulletCategory) won't match.

Storing story data for text adventure AS3

I've been on and off creating a text adventure using a rudimentary engine I made for about a year.
For the story, I have a object array(is that what it's called?) with various story data stuff that I parse through
I've been told that using it the way I am is stupid because it's supposed to be used for other stuff but I only use it because it was easy to learn how to parse the data since I was a beginner
It's getting tedious writing the story and doing stuff for each part (creating the backgrounds and such) since it's so long.
Is there any kind of way I can make it easier for me to write the story?
Here's the object array with a single part set up (with choices)
public static var parts:Object =
{
"0":
{
"text":"Text here",
"choices":
{
"response1":
{
"text":"Response1",
"nextPart":"1"
},
"response2":
{
"text":"Response2",
"nextPart":"2"
}
},
"background": Assets.AssetClass.Background1,
"BGM":"bg1"
},
}
Here's an example of how my engine deals with parts and changing them:
I have a input checker to check when enter is pressed and then do stuff depending on what is on the screen
public function onEnter(button:KeyboardEvent):void
{
if (button.keyCode == 32 && !Won)
{
if (Dead && textFinished && !choosing) // pressing enter while dead and the text is still writing
{
curPart = parts[curPart]["lastPart"] // lastPart will only be stored in parts that have the player die
textFinished = false
Dead = false;
myTextField.text = ""
counter = 0;
sInt = setInterval(addCharackter, textSpeed)
if (stage.getChildByName("cText"))
{
stage.removeChild(continueText)
}
if (parts[curPart].hasOwnProperty("background")) //check if the background needs to change.
{
if (stage.getChildByName("img"))
{
stage.removeChild(background)
}
background = new Background(parts[curPart], 800, 600)
stage.addChildAt(background, 0)
}
}
if (!textFinished && !choosing)// pressing enter when there's no choices on the screen and the text isn't finished and the text is still writing
{
this.myTextField.text = this.parts[this.curPart]["text"];
clearInterval(this.sInt);
this.textFinished = true;
if (parts[curPart].hasOwnProperty("choices"))
{
choosing = true
createOptions(); // function for parsing through the responses bit of that part and displaying them
}
else
{
stage.addChildAt(continueText, 2)
}
if (parts[curPart].hasOwnProperty("lastPart"))
{
Dead = true;
dead()
}
}
else if (textFinished && !choosing && !Dead) // pressing enter if there's no choices on the screen and there's no choices (it'll take you to the next part)
{
trace("Text finished!!")
curPart = parts[curPart]["nextPart"]
myTextField.text = ""
counter = 0;
sInt = setInterval(addCharackter, textSpeed)
textFinished = false;
if (parts[curPart].hasOwnProperty("background"))
{
if (stage.getChildByName("img"))
{
trace("Removed!")
stage.removeChild(background)
}
background = new Background(parts[curPart], 800, 600)
stage.addChildAt(background, 0)
}
if (parts[curPart].hasOwnProperty("BGM")) // does the current part have a new background music?
{
trace("Music!!!!")
sndBGMusic = musicArray[parts[curPart]["BGM"]]
sndBGMusicChannel.stop()
sndBGMusicChannel = sndBGMusic.play(0, 9999999999)
stage.addChildAt(background, 0)
}
stage.removeChild(continueText)
}
}
}
A couple ideas here. These are just things I would do differently than what you have done. I don't guarantee that they are better in any way, but see what you think.
I would have a global variable for _curPart. And I would have a custom class called Part. That class would have properties like _BGM, _bgImage etc. It could have a _choicesArray as a property as well. I'd have other global variables like _hasCandle. Or you can store items in an array and if you need the candle just check if candle is in the array. These global variables will persist from one part to the next.
Then you can access the properties of the part you are in by doing _curPart._bgImage. To me, that looks and feels cleaner.
And to create a new part it could look like (incomplete):
var p15:Part = new Part();
p15._bgImage = image15;
p15._BGM = song10;
//...
The last thing I'd recommend is to try to refactor where you can. For example, where you have //pressing enter if there's no choic... replace all of that code in that bracket with a one or a few function calls (whatever makes the most sense and allows you to reuse code). It just makes it easier to see what's going on, I think. So instead of all these if blocks, just a function like nextPart(); and then that function will have all your if blocks in it. Make sense? Personal preference, but when things are getting complicated, refactoring helps me clear out the cobwebs. Just like you do with dead() and createOptions() but I'd just take it one step further. This won't make your code more efficient, but it might make writing your code more efficient which is paramount in my book (until it's not).

AS3 Check if no object in an array is being hitTested

I have an array of platforms that I'm hitTesting against a player. I also have a boolean variable called onGround that is attached to the player.
I need to be able to toggle onGround based on whether or not the player is hitting a platform in the array. It's been easy to check if the player is hitting a platform with:
//loop through array
var platform = platformArray[i];
if(player.hitTestObject(platform)){ onGround = true;}
Unfortunately, checking if the player is not hitting a platform has caused a lot of confusion. Even with:
if(!player.hitTestObject(platform)){ onGround = false;}
because of logic, if the player would be touching a platform, it would also not be touching another platform and the above line would still execute. onGround will continually swap between true and false.
I need to be able to check if all of the platforms in the array are not being hit at the same time. Dozens of my own solutions have mostly failed me. Is the player hitting at least 1 platform in the array, or none at all? Any ideas?
Your first method is sufficient. You only need to check whether onGround is false after the loop, i.e. after testing against all platforms.
// set initial value of onGround to false
onGround = false;
//loop through array
var platform = platformArray[i];
if (player.hitTestObject(platform)) {
onGround = true;
break;
}
// after loop
if (onGround) {
// at least one is hit
} else {
// none is hit
}
A better approach will be to wrap the logic inside a function. Something like this:
function isAnyPlatformHit(platformArray, player):Boolean {
for (var i:int = 0; i < platformArray.length; i++) {
var platform = platformArray[i];
if (player.hitTestObject(platform)) {
return true;
}
}
return false;
}
if (!isAnyPlatformHit(platformArray, player)) {
// none hit
}

Best way to end a function that contains an array in AS3

So I'm real new to AS3 and I'm trying to figure out a solution to end a function I created for a matching game. I want to have the function end when all the cards are used up in the array. What is the easiest way to go about this?
private var games:Object = {
easy:{
tiledeck:[1,1,2,2]
,xOffset:450
,yOffset:320
,incrementX:200
,incrementY:200
,columns:2
,rows:2
}
,hard:{
tiledeck:[1,1,2,2,3,3,4,4]
,xOffset:235
,yOffset:320
,incrementX:200
,incrementY:200
,columns:4
,rows:2
}
};
public function KT(game:String){
buttonMode = true
var gameConfig = games[game];
var tiledeck:Array = gameConfig.tiledeck.concat();
for (var x=1; x<=gameConfig.columns; x++){
for (var y=1; y<=gameConfig.rows; y++){
var random_card = Math.floor(Math.random() * tiledeck.length);
var tile:animalTile = new animalTile();
tile.animal = tiledeck[random_card];
tiledeck.splice(random_card,1);
tile.gotoAndStop(5);
tile.x = (x - 1) * gameConfig.incrementX + gameConfig.xOffset;
tile.y = (y - 1) * gameConfig.incrementY + gameConfig.yOffset;
tile.addEventListener(MouseEvent.CLICK,tile_clicked);
addChild(tile);
}
}
}
Your problem is not what you think it is.
The array is properly discarded from memory.
However, you used addChild(tile). This means you'll also have to removeChild(tile). Personally, I recommend adding a DisplayObjectContainer that you add the cards to. Kinda like a plastic sheet to put the cards on. Then, when the user presses the back button, you remove the plastic sheet... and all the cards come with it.
You haven't provided the code for the back button though, so I can't help you with integrating this functionality. My advice: Make some sort of game object responsible for cleanup, so all the button has to do is game.exitGame(); and then whatever code you use to go back right now.

Resources