I have a game that uses the camera view, and has enemies flying around the screen. You then shoot the enemies using your chosen weapon. At the moment I have 4 UIImageView's, in which the enemies are animated. I am wanting to have multiple instances of each enemy (there are 4) on the screen, and with time more spawn. What is the best way to do this?
This is part of the code to move one enemy using the gyro: (enemyCyborg is the UIImageView)
valueX4 = gyroData.rotationRate.y* 30;
valueY4 = gyroData.rotationRate.x* 40;
int newX4 = (int)(enemyCyborg.center.x +valueY4);
int newY4 = (int)(enemyCyborg.center.y -valueX4);
CGPoint newCenter4 = CGPointMake(newX4, newY4);
enemyCyborg.center = newCenter4;
And they are moving randomly within that:
- (void) moveCyborg {
[UIView animateWithDuration:1.0f animations:^{
int fromNumber = -30;
int toNumber = 60;
int randomNumber = (arc4random()%(toNumber-fromNumber))+fromNumber;
//Move the image view to 100, 100 over 10 seconds.
enemyCyborg.frame = CGRectMake((int)(enemyCyborg.center.x -randomNumber), (int)(enemyCyborg.center.y -randomNumber), enemyCyborg.frame.size.width, enemyCyborg.frame.size.height);
}];
}
This shows the UIImageView again, after it has been hidden from shooting it:
- (void) showCyborg {
enemyCyborg.hidden = NO;
enemyCyborg.center = CGPointMake((arc4random()%SCREEN_HEIGHT),(arc4random()%SCREEN_WIDTH));
enemyCyborg.animationImages = cyborganim;
enemyCyborg.animationDuration = 0.6;
enemyCyborg.animationRepeatCount = 0;
[enemyCyborg startAnimating];
}
You'll want to create objects encapsulating the enemy model and spawn those objects using some kind of enemy generator function. This generator function might be a written as a class function.
Here are some general suggestions:
1) You're using the block-based animation methods. Blocks are a good approach but depending on your performance and the number of enemies and other visual elements you plan to add, you may want to look into using Cocos2d which gives you all sorts of features. Ray Wenderlich has published a great intro to cocos2d tutorial that would get you up and running quickly. Link to that here. Many games for iPhone are written using either cocos2d or Unity.
2) Check in https://gamedev.stackexchange.com/ for further basic examples, if you haven't done so already.
Related
I'm trying to move a sprite in a set direction on-screen, using the physicsBody.velocity property. However, the motion is quite "jumpy" (makes the game look like it's lagging). I assume this has something to do with the different time intervals between each call to the update-method. How could I achieve a more "smooth" motion?
Right now this is the code I have in my update-loop to make the sprite move forward:
if (_movingForward) {
CGFloat forwardVel = _ball.physicsBody.velocity.dx;
forwardVel += 20.0;
if (forwardVel > bMaxForwardVelocity) {
forwardVel = bMaxForwardVelocity;
}
_ball.physicsBody.velocity = CGVectorMake(forwardVel, _ball.physicsBody.velocity.dy);
}
As you can see, I'm trying to make the speed pick up, so it doesn't immediately reach it's terminal velocity. This is to make the motion look more natural. However, the lagging is ruining this effect greatly, even when the sprite supposedly is traveling at a set speed.
EDIT 1:
I'm trying to make a platform-style game (like Super Mario etc) where the _ball moves horizontally along the ground when the user taps the screen. The code for moving the ball to the right is included above the edit. The ground is made up of multiple tiles, each 32x32 and positioned right next to each other with no gap in-between.
As per request, here's the code for setting up the _balls physicsBody:
self.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:self.size.width/2.0];
self.physicsBody.dynamic = YES;
self.physicsBody.restitution = 0.5f;
self.physicsBody.usesPreciseCollisionDetection = YES;
self.physicsBody.allowsRotation = NO;
self.physicsBody.mass = 0.5f;
self.physicsBody.friction = 0.0f;
self.physicsBody.linearDamping = 0.0f;
self.physicsBody.categoryBitMask = CollisionCategoryBall;
And here's the code for the physicsBody of each individual tile:
tile.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:tile.size];
tile.physicsBody.dynamic = NO;
tile.physicsBody.categoryBitMask = CollisionCategoryGround;
The physicsWorld is set up like this:
self.physicsWorld.gravity = CGVectorMake(0, -4);
self.physicsWorld.contactDelegate = self;
Thanks!
EDIT 2:
This is a side-scrolling game, so the "camera" follows the ball. Here's the code for moving the background layer:
- (void)centerViewOn:(CGPoint)centerOn {
CGFloat x = Clamp(centerOn.x, (self.size.width / 2.0), (_bgLayer.layerSize.width) - (self.size.width / 2.0));
_worldNode.position = CGPointMake((int)-x, _worldNode.position.y);
}
The reason for the Clamp is so the "camera" doesn't scroll beyond the bounds of the background. This is when the ball is close to either the start or end of the level.
I'm casting the position to an int because without it, small gaps sometimes occur between the tiles. I was told this was because of some inaccuracy or something. However, even without casting the position to int, it's still a bitt "laggy". But because of the gaps between the tiles, it looks even worse.
The issue was with your scrolling code.
change this line :
_worldNode.position = CGPointMake((int)-x, _worldNode.position.y);
to
_worldNode.position = CGPointMake(-x, _worldNode.position.y);
The movement is then silky smooth.
But then you do get those seams. You should solve that in the placement of those tiles. You are likely positioning your _worldNode tiles with float values.
I'm currently making my first sprite game and one of the things i have implemented into my game is objects coming in from the side, move all the way across the screen, and then be removed from the parent. Here's what that looks like.
MyScene.m
-(void)createObstacle0 {
CGPoint startPoint = CGPointMake(-20, 100);
SKSpriteNode *obstacle = [SKSpriteNode spriteNodeWithImageNamed:#"obstacle"];
obstacle.position = CGPointMake(startPoint.x, startPoint.y);
obstacle.name = #"obstacle0";
[self addChild:obstacle];
float randomNum = arc4random_uniform(2) + 1.92;
[self performSelector:#selector(createObstacle0) withObject:nil afterDelay:randomNum];
}
excerpt from update method
[self enumerateChildNodesWithName:#"obstacle0" usingBlock:^(SKNode *node, BOOL *stop) {
if (node.position.x > 360) {
[node removeFromParent];
} else {
node.position = CGPointMake(node.position.x + 5.2, node.position.y);
}
}];
At the moment, my objects only move across the screen at one speed. I tried using arc4random inside the currentTime method code, but because thats evaluated every frame, my object would not stay at one speed. It would speed up, slow down, and look very erratic. How can i change my code in CurrentTime to allow my objects crossing the screen to be set a random speed as soon as its made and to keep it until it is destroyed?
Second question:
int yMin = 90;
int yMax = 110;
CGPoint startPoint = CGPointMake(-20, yMin + arc4random_uniform(yMax - yMin));
I would suggest that instead of manually updating the child nodes position to move it from one side to another you create your obstacle and then call runAction: on it using an SKAction created with the moveTo:duration: method. You specify the final point you want your obstacle to end up at as well as how long you want this move to take. Pass in a random number with in the minimum and maximum time you want it to take to duration.
This should allow you to create consistent but randomised object movement.
int MAX_TIME = 20;
int MIN_TIME = 10;
[obstacle runAction:[SKAction moveTo:CGPointMake( 10, 100 ) duration:MIN_TIME + arc4random_uniform( MAX_TIME - MIN_TIME )]];
I asked a question recently that sort of involved implementing power-ups; however, I have decided that I want to go about it a different way.
In my game, I have an endless scrolling background and the main character moving to the right while collecting coins.
When the player hits the blue coin, the power-up, I want 1. the character to change to a blue color (I have the frames for this), 2. the background to go blue, and 3. the platform to change blue (have images for this); I would like all these factors to take place for a 20 second period of time.
I planned to do this by having a Blue class with a blue instance variable (public variable) that I would set to YES and NO accordingly (if a blue coin has been hit) in my other classes (Platform class, Player class). However, it is not efficient and does not work for when I incorporate a timer.
Does anyone have an idea on how to implement the power-up?
This is my code for when the blue coin is hit by the player:
// try remove blue coin
- (void) tryRemoveBluecoin
{
NSMutableArray * currentBluecoinArray = [self getcurrentBluecoinArr];
if(currentBluecoinArray)
{
int playerY = ((CCLayer*)(self.player)).position.y;
for(int x=0; x<[currentBluecoinArray count];x++)
{
CCSprite *bluecoin = [currentBluecoinArray objectAtIndex:x];
if(abs(bluecoin.position.x+bluecoin.parent.position.x-[Player initX])<50)
{
if(abs(bluecoin.position.y+bluecoin.parent.position.y-playerY)<30 && bluecoin.visible && bluecoin.visible)
{
[bluecoin.parent removeChild:bluecoin cleanup:YES];
CGSize winSize = [[CCDirector sharedDirector] winSize];
[[SimpleAudioEngine sharedEngine] playEffect:#"jump.wav" pitch:1 pan:0 gain:1];
// SET BLUE VARIABLE TO YES
NSLog(#"BEGIN BLUE POWER UP EFFECTS FOR 20 SECONDS");
}
}
}
}
[self hitTestOB];
}
Thanks for any ideas you have!
Now, it has been some time since I last used Cocos2d, but you are aware that you can actually set color-information to nodes, right? This sounds like a much more sensible way to go down. Design your sprites with this in mind and keep an array with all elements you need to color.
Have a method defining the new color based on your game state, the game state enums can be matched with an NSInteger property on your power-ups for instance.
ccColor3B color;
// _state being an NSInteger ivar
// the different states defined in an enum
switch (_state) {
case gameStateBlue:
color = ccc3(0, 0, 255);
break;
case gameStateGreen:
color = ccc3(0, 255, 0);
break;
default:
break;
// etc.
}
send this color information to a method which handles your array of sprites like this:
for (CCSprite *sprite in _arrayOfSpritesToChangeColor) {
sprite.color = color;
}
This will demand planning ahead with your assets, but a lot more flexibility down the line if you want to experiment with different colors and effects. It is a lot less taxing as well as you won't need to swap a bunch of assets to achieve what you want. Now, my Cocos is rather rusty so I might have messed some details up, but the general idea should be sound.
Edit: An alternative to holding reference to the sprites in an array is to have your own sprite subclass and let that subclass have a CCSprite colorSprite property. Then you could loop through the children of your scene and change the sprites that have this property.
I am creating an app where there will be ~10 circles on screen, that can all be pushed and dragged around the screen via touch movements. These circles (which are simply UIView's with a circle drawn in them) also have collision detection on them, and the idea is that they push other circles out of the way when they collide. There is also no acceration or gravity in this app, it's more like pushing coins around, where they are simply pushed out of the way, and when you stop moving, so do they.
I have touch-to-circle interaction working, and the fundamentals of circle-to-circle working. The code however gives Circle01 all the power. IE, it can be pushed into other circles and they will move out of the way as hoped. But when the other circles are pushed into it, they move around Circle01, instead of pushing it out of the way.
I've read dozens of pages about collision detection, but I'm just stumped as to how I'd modify this code to give all the circles equal power.
Relevant code:
MyCircle.m
- (void)drawRect:(CGRect)rect
{
CGPoint viewCenter = CGPointMake(50,50);
bpath = [UIBezierPath bezierPathWithArcCenter:viewCenter radius:50 startAngle:0 endAngle:DEGREES_TO_RADIANS(360) clockwise:YES];
[[UIColor blueColor] setFill];
[bpath fill];
}
CircleVC.m
- (void)viewDidLoad
{
Circle01 = [[MyCircle alloc] initWithFrame:CGRectMake(200, 200, 100, 100)];
Circle01.backgroundColor = [UIColor redColor];
Circle01.alpha = 0.5;
[self.view addSubview:Circle01];
}
-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
// Testing circle collision detection
for (UIView *aView in self.view.subviews)
{
if (Circle01 == anotherView)
continue;
if (CGRectIntersectsRect(Circle01.frame, aView.frame))
{
float xDistance = aView.center.x - Circle01.center.x;
float yDistance = aView.center.y - Circle01.center.y;
float distance = xDistance * xDistance + yDistance * yDistance;
float angle = atan2(yDistance, xDistance);
CGPoint newCenter;
if (distance < 100 * 100)
{
newCenter.x = Circle01.center.x + (Circle01.frame.size.width * cos(angle));
newCenter.y = Circle01.center.y + (Circle01.frame.size.width * sin(angle));
aView.center = newCenter;
}
}
}
}
Thanks for any help.
It may be easier to offload this to a physics engine such as Box2d. Cocos2d is packaged with Box2d, but you will want the stand alone version since you are only using UIView based animations
_
Option 1
Here is a good, though slightly dated tutorial on using Box2d with UIKit.
http://www.cocoanetics.com/2010/05/physics-101-uikit-app-with-box2d-for-gravity/
Some caveats: if you are using ARC you will need to use the god-awful bridging compiler hints that tell the ARC pre-compiler when your C objects are being put into and out of its control.
For example:
bodyDef.userData = (__bridge void *)physicalView;
UIView *oneView = (__bridge UIView*)b -> GetUserData();
_
Option 2
You can use Chipmunk (which would also require the above "god-awful" bridging to ARC statements). Here is a good tutorial: http://www.alexandre-gomes.com/articles/chipmunk/
_
Option 3 - Easiest (Not Cheapest)
Alternatively, you could use the Objective-Chipmunk library that comes with Chipmunk Pro which does work with ARC. Here is a recent and "to-the-point" tutorial on using Obj-Chipmunk with UIImageViews. This would be the same for any UIView based code. http://www.ayarsanimation.com/frankayars/chipmunk
You can buy Obj-Chipmunk costs $89 when you by an Indie version license for Chipmunk Pro.
http://chipmunk-physics.net/chipmunkPro.php
I am not advocating a paid solution, but it may save you time.
It seems pretty simple, unless I'm missing something. Instead of hard-coding Circle01 in your touchesMoved, do your hit detection based on the view that the user touched. That way, whatever view they are dragging will get priority.
I have an iOS game, and when a ball hits a target it explodes.
What would be the best way to animate this explosion?
If you're looking for something simple.
Make a series of images that animate the explosion. Add those to a UIImageView and start the animation. Something like this:
UIImage *image0 = [UIImage imageNamed:#"explosion0.png"];
UIImage *image1 = [UIImage imageNamed:#"explosion1.png"];
UIImage *image2 = [UIImage imageNamed:#"explosion2.png"];
UIImage *image3 = [UIImage imageNamed:#"explosion3.png"];
myImageView.animationImages = [NSArray arrayWithObjects:image0, image1, image2, image3, nil];
[myImageView setAnimationDuration:0.75f];
[myImageView startAnimation];
For a bouncing ball game, the Cocos2D game- and Box2d physics- (included with Cocos2D) engines are a great way to go.
Ray Wenderlich has three great tutorials on exactly what you want to do:
Intro to Box2D with Cocos2D Tutorial: Bouncing Balls
How To Create A Breakout Game with Box2D and Cocos2D Tutorial: Part 1
How To Create A Breakout Game with Box2D and Cocos2D Tutorial: Part 2
Once you get past the basics of working with Box2d objects, the key step is to create a Contact Listener (as described in the tutorials) which will tell you when you have a collision. Check (by iterating over contacts in your tick method) if the colliding objects are the ones you want for the explosion, and if so add them to a vector of objects to be destroyed (we can't do this while iterating over contacts or we will invalidate the iterator). Remember the coordinates of the collision and set a flag for explosion.
After exiting the loop destroy the bodies and run the explosion effect at the saved position.
Cocos2D has some very nice built-in explosion effects, and the one I am using for my project is CCParticleSun. You can find some screenshots of someone using this effect here. (The curiously named CCParticleExplosion just gave a lot of confetti flying around the screen.)
Here is the relevant portion from some preliminary code I have working, which is mostly from the tutorial. You will still need to do everything else, eg. create and set up your world and bodies in your init method, update positions, apply forces, and draw in your tick method, implement your Contact Listener class, etc. For all this refer to the tutorials.
std::vector<b2Body *>toDestroy;
std::vector<MyContact>::iterator pos;
CGPoint p;
bool explode = FALSE;
for(pos = _contactListener->_contacts.begin();
pos != _contactListener->_contacts.end(); ++pos) {
MyContact contact = *pos;
if ((contact.fixtureA->GetBody() == _moonBody && contact.fixtureB->GetBody() == _moon2Body) ||
(contact.fixtureA->GetBody() == _moon2Body && contact.fixtureB->GetBody() == _moonBody)) {
NSLog(#"Moons collided!");
explode = TRUE;
p = CGPointMake(PTM_RATIO*(_moonBody->GetPosition().x + _moon2Body->GetPosition().x)/2.,PTM_RATIO*(_moonBody->GetPosition().y + _moon2Body->GetPosition().y)/2.);
if (std::find(toDestroy.begin(), toDestroy.end(), _moonBody) == toDestroy.end()) {
toDestroy.push_back(_moonBody);
}
if (std::find(toDestroy.begin(), toDestroy.end(), _moon2Body) == toDestroy.end()) {
toDestroy.push_back(_moon2Body);
}
}
}
std::vector<b2Body *>::iterator pos2;
for(pos2 = toDestroy.begin(); pos2 != toDestroy.end(); ++pos2) {
b2Body *body = *pos2;
if (body->GetUserData() != NULL) {
CCSprite *sprite = (CCSprite *) body->GetUserData();
[sprite.parent removeChild:sprite cleanup:YES]; }
world->DestroyBody(body);
}
if (explode)
{
explosion = [[CCParticleSun alloc]initWithTotalParticles:250];
explosion.texture = [[CCTextureCache sharedTextureCache] addImage:#"fire.png"];
explosion.autoRemoveOnFinish = YES;
explosion.startSize = 15.0f;
explosion.speed = 30.0f;
explosion.position = p;
explosion.duration = 1.0f;
[self addChild:explosion z:self.zOrder+1];
}
What you want, then, is a way to create a particle emission system. My suggestion is to use a CAEmitterLayer. Ray Wenderlich has a great tutorial: UIKit Particle Systems in iOS 5 Tutorial
iPhone Programming Tutorial: Animating a Game Sprite
If you need to make an explosion another alternative would to use Cocos2d and do:
CCParticleSun *anExplosion=[CCParticleGalaxy node];
[kk setPosition:ball.position];
[self addChild:anExplosion];
Not really an animation, also, from the question you don't seem to be using Cocos2d, but if you start using it you can do this...