I'm throwing a lot of objects across the screen (1 beachball every 0.1s) and swiping them toward a box, the balls are recycled after they leave the screen or hit the ball. The problem i encountered is that when multiple balls hit the box, frame rates drop drastically. I'm not sure what's making this happen. Is there a better way to do the following so that it doesn't lag? I use the default cocos library to play sounds.
-(void)ccPhysicsCollisionPostSolve:(CCPhysicsCollisionPair *)pair can:(CCNode *)nodeA wildcard:(CCNode *)nodeB {
if([nodeB.physicsBody.collisionType isEqualToString:#"hit"]){
[[_physicsNode space] addPostStepBlock:^{
[self remove:nodeA];
} key:nodeB];
}else if([nodeB.physicsBody.collisionType isEqualToString:#"floor"]){
[[_physicsNode space] addPostStepBlock:^{
[self remove:nodeA];
} key:nodeB];
}else {
[self playSound:#"collide.mp3" bg:YES];
}
}
Related
I'm using Lance for a game where the gameplay area is a tiled map. When a player presses the left-arrow key, their character should move one tile to the left, etc. I tried two approaches, see below, but got neither to work.
Could either approach be modified to work with tile-based movement? Or is a third approach needed? Or is Lance not suited to this kind of game?
Approach 1: Adjust the player's position directly when a key is pressed. From my GameEngine class:
if (inputData.input == 'left') {
player.position.x -= 32;
player.angle = 180;
}
While this works well for a single player, it doesn't in multiplayer. When player A moves, their position is not updated on player B's screen.
Approach 2: Set the player's state when a key is pressed:
if (inputData.input == 'left') {
player.state = 'walkLeft';
}
Then add a postStep handler in the common GameEngine class. (Adding it to Player didn't work). This code turns the player (over many steps) to face 180 degrees and then accelerates the player in that direction:
onPostStep(event) {
let players = this.world.queryObjects({instanceType: Player});
players.forEach(player => {
if (player.state == 'walkLeft') {
if (Math.abs(player.angle - 180) > 2)
player.turnLeft(2);
}
else {
player.accelerate(1);
player.state = '';
}
}
})
}
With this approach, if a player presses the left arrow key, their angle changes as expected at first, but the acceleration and movement is erratic. Also, Player A's position appears different on their screen vs the screen of Player B.
The Spaaace demo is the base for my project, so my project uses the same bending, physics engine, etc.
The first approach is better. The Brawler game in the sample collection does exactly what you describe. You can look at the BrawlerGameEngine.js code in https://github.com/lance-gg/tinygames/tree/master/brawler
Make sure that the action is processed in the method
GameEngine::processInput(inputData, playerId)
I'm working on a game where if there is a collision between two of the same bodies then a sound plays. I've got this part working fine, but I only want the sound to play if the collision happens in the view. I have a scrolling background and collisions further ahead are making a noise.
Is there anyway to limit this sound from only playing when a collision happens in view?
Right now i'm using this code:
let rocksCollide = SKAction.playSoundFileNamed("rocks.wav", waitForCompletion: false)
if nodeB.name == "SMALLASTEROID" && nodeA.name == "SMALLASTEROID"{
runAction(rocksCollide)
}
Any help would be appreciated.
You can check if the node.position is inside the visible frame. For example.
if (CGRectContainsPoint(visibleFrame, nodeA.position)) {
// Play sound.
}
I'm relatively new to SpriteKit and am wondering what alternatives there are to using the CACurrentMediaTime() for scheduling events. For example, I may implement an algorithm that prevents a player from firing too many times by adding the last time they fired with some 'cool down' period, comparing it to the current media time:
BOOL canFire = self.lastFireInterval + self.coolPeriod < CACurrentMediaTime();
The problem I have run into is that if I decide to alter the speed of a node, or even the entire scene, this logic falls apart. For example, if I gave the player a speed-up power up, I could slow down all other nodes except for the player, but the timings would be messed up for enemies firing.
What other alternatives are there for CACurrentMediaTime() that factor in the speed of the node?
2 options come to mind regarding the timed to fire:
1) Create an ivar/property BOOL readyNextMove; for your player class. Then when your player shoots set the readyNextMove to false and add this code:
SKAction *wait = [SKAction waitForDuration:2.0];
SKAction *block0 = [SKAction runBlock:^{
readyNextMove = true;
}];
[self runAction:[SKAction sequence:#[wait, block0]]];
2) You can use the update:(CFTimeInterval)currentTime method to see how much time has elapsed from, for example you player shooting, and set readyNextMove accordingly.
It's a matter of taste. But for all games I keep an internal frame based clock which handles everything. I firmly believe this is the best way, but of course it depends on the scope of your project. This method is very rigid and guarantees continuity. If you use timers and your game lags, your continuity is thrown off (in some cases a shooter might shoot more times per frame than other parts of your game gets to move).
In aScene.h
long _currentTime = 0;
In aScene.m
-(void) update {
_currentTime++;
for(shooter * aShooter in self.shooters) {
[aShooter updateWithTime:_currentTime];
}
}
In your shooter.m
-(BOOL) tryShoot:(long)globalTime {
if(_lastShootingTime<globalTime-shootingCooldown) {
_lastShootingTime = globalTime;
[self fireBullet];
return YES;
}
return NO;
}
-(void) updateWithTime:(long)globalTime {
//...perform shooters action for that time frame
_lastUpdatedTime = globalTime;
}
I already know how to check for collisions with the doesintersectNode-method in Cocos3d, but in my case, I want to avoid obstacles, before I get in touch with them. In example, I want to stop in front of a wall, before I crash against it.
For this reasons I wrote the methods getNodeAtLocation in my subclass of CC3Scene and -(BOOL)shouldMoveDirectionallywithDistance:(float)distance in the class of my person, which should move around.
Unfortunately, I have some problems with the algorithm of the last method. Here the code:
-(BOOL)shouldMoveDirectionallywithDistance:(float)distance
{
BOOL shouldMove = NO;
float x = self.person.globalLocation.x;
float z = self.person.globalLocation.z;
int times = 5;
for (int i = 0; i < times; i++) {
CC3Vector newPos = cc3v(x, 0.5, z);
CC3PODResourceNode *obstacle = (CC3PODResourceNode *)[myScene getNodeAtLocation:newPos];
if (obstacle) {
return NO;
}else{
shouldMove = YES;
}
x += self.person.globalForwardDirection.x * distance / times;
z += self.person.globalForwardDirection.z * distance / times;
}
return shouldMove;
}
In this method, I get the important parts of the coordinates (for my proposal just the x- and z-values) and increase them by a fifth of the forwardDirection. I decided, that this makes sense, when the obstacle is i.e. a thin wall. But for reasons I don't know, this method doesn't work, and the person is able to walk through this wall. So where is the problem in my code?
I strongly believe, that the getNodeAtLocation-method works correctly, as I tested it multiple times, but maybe there are my mistakes:
-(CC3Node *)getNodeAtLocation:(CC3Vector )position
{
CC3Node *node = nil;
for (CC3PODResourceNode *aNode in self.children) {
if ([aNode isKindOfClass:[CC3PODResourceNode class]] ) {
for (CC3PODResourceNode *child in aNode.children) {
if (CC3BoundingBoxContainsLocation(child.globalBoundingBox, position)) {
node = aNode;
}
}
}
}
return node;
}
To conclude, in my view the mistake is in the -(BOOL)shouldMoveDirectionallywithDistance:(float)distance-method. I suppose, that something is wrong with the increase of the x- and z-values, but I couldn't figure out, what exactly is incorrect.
If you are still interested in finding an answer to this problem. I may be able to provide you with an alternative solution. I am about to release free source for a 3d collision engine I ported to cocos3d, and it will give you more flexibility than simply stoping an object in front of another.
I am currently polishing out the code a little for easy use, but if you are interested you can email me to: waywardson07#aol.com
you could also get a little preview of the engine in action here: http://www.youtube.com/watch?v=QpYZlF7EktU
Note: the video is a little dated.
After several attempts, it appears to be easier as I thought:
Just using the doesIntersectNode method has the right effect for me.
But please notice, that this is not a real solution to the problem of stopping in front of obstacles.
I'm creating an app for iOS (mainly) in Flash CS6 and I'm having a few problems with getting a particular page to work.
The layout is as follows: I have a movie clip that is 3 times the width of the stage with my content, with the instance name of txtContent.
On a separate layer, my Action Script (v3.0) reads as follows:
import com.greensock.*;
import flash.events.MouseEvent;
//Swipe
Multitouch.inputMode = MultitouchInputMode.GESTURE;
var currentTile:Number = 1;
var totalTiles:Number = 3;
txtContent.addEventListener(TransformGestureEvent.GESTURE_SWIPE , onSwipe);
function moveLeft():void{
txtContent.x += 640;
}
function moveRight():void{
txtContent.x -= 640;
}
function onSwipe (e:TransformGestureEvent):void{
if (e.offsetX == 1) {
if(currentTile > 1){
moveLeft()
currentTile--
} else {}
}
if (e.offsetX == -1) {
if(currentTile < totalTiles){
moveRight()
currentTile++
}
}
}
stop();
When I test the movie, with a touch layer, the movie clip successfully moves left and right for each swipe, and does not continue to move too far in either direction, in effect ignoring any other swipes.
However, when I compile the IPA and test on the iPhone, only the first two "tiles" move (I can only see two thirds of the movie clip with swiping), as if I swipe to the third "tile" I cannot swipe back at all. No matter what I do, it gets stuck on that third section.
Is there a problem in my code that isn't registering properly in iOS?
FYI, I'm testing on an iPhone 3GS.
It was my own mistake - the final 'page' of the slides did not have a large background with the alpha set to 0% like the others, therefore to slide it back it would only work when holding the text (which was small). With the addition of the background, the movieclip is solid and therefore swiping the whole screen worked fine.