HiveBrain v1.2.0
Get Started
← Back to all entries
patternModerate

Game with tile map - Sprite-Kit

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
mapspritewithkitgametile

Problem

I'm a novice programmer learning Objective-C in my spare time. I would greatly appreciate any help or advice with my code. I want to follow best practices whenever possible. I know that I am missing some foundational stuff with my code, so please point that out where you see it. Things like when to make variables instance variables versus properties, when to define things like (strong) for properties, when and where to instantiate variables, etc.

I'm building a simple game to practice and to learn Sprite-Kit. I've been working on it for a few days, and it does function mostly as I want it to. The game model is pure objective-c and generates a map of tiles as well as handles inserting tiles into the map. This is done with an NSDictionary with the keys being the coordinates of the tiles. That code is not included below, but I can post it if need be. My SKScene renders those tiles based on their type, and tells the model to insert new tiles where the screen is tapped. It also manages the touch controls.

The main problems are that the panning around the world and the pinching to zoom move much faster than I would like. I can't figure out how to slow them down, and I'm wondering if I am doing something wrong.

Please let me know if you need to see more code to review it. And please don't hesitate to point out any flaws that you see, both in style and in substance.

Here is the header:

//  TPGameSceneSO.h

#import 
#import "TPGame.h"

//set up for 60 frames per second
#define kMinTimeInterval (1.0f / 60.0f)

//seems to run better with the delegate enabled
@interface TPGameSceneSO : SKScene 

@property TPGame *theGame;
@property SKSpriteNode *tileMapNode;
@property SKSpriteNode *movableSprites;
@property UIPinchGestureRecognizer *pinchRecognizer;
@property UIPanGestureRecognizer *panRecognizer;

@end


And now the implementation:

```
enter code here
// TPGameSceneSO.m

#import "TPGameSceneSO.h"

#define TILE_SIZE CGSizeMake(128,128)
#define TILE_DI

Solution

Instance Variables and Properties

Understanding exactly what a @property is is absolutely crucial to being a good Objective-C programmer. Here's my crash course.

An instance variable is just that. An instance variable. It's pretty straight forward. It's just a variable in that can be accessed by all the methods in your class. And if it's a public variable, it can be accessed outside the class as well.

A @property, however, is different. A @property doesn't even necessarily have an instance variable behind it. It does by default, but a @property is much more than just an instance variable. Moreover, you can have public AND private @property variables. Simon's comments on properties versus variables are inaccurate.

Consider

@interface Person : NSObject
@property (nonatomic, strong) NSString *name;
@end


What have we actually done? As it turns out, this is the equivalent of doing this:

@interface Person : NSObject {
    @private
    NSString *name;
}
- (void)setName:(NSString*)aName;
- (NSString*)name;
@end

@implementation Person
- (void)setName:(NSString*)aName {
    name = aName;
}
- (NSString*)name {
    return name;
}


So by default, a @property creates three things: an instance variable, a setter, and a getter.

But consider this case:

@interface Person : NSObject
@property NSString *firstName;
@property NSString *lastName;
@property (readonly) NSString *fullName;
@end

@implementation Person
- (NSString*)fullName {
    return [NSString stringWithFormat:"%@ %@", self.firstName, self.lastName];
}
@end


By declaring fullName as a readonly property, we've prevented the creation of a setter method. But in the @implementation, what we've also done is overridden the getter method. But something very important happened in this method. We made no reference to _fullName, which would be the instance variable that a @property called fullName would create. As such, we've also prevented the creation of an instance variable.

So, the other thing left is that a @property allows you to use the dot-syntax to access the setters and getters (though you don't have to use it).

Now then, when should you use an @property and when should you use an instance variable?

Well, it really doesn't matter too terribly much. If you need a variable to be visible outside the class, you can still use an instance variable and still give it a getter and setter. The only difference between writing a getter, setter, creating an instance variable and just using a @property? Dot-syntax, that's it.

And the cool thing is, you don't have to limit yourself to the traditional idea of a setter, getter, and instance variable. You can create dot-syntax methods any time you need a method that takes no arguments and returns a value.

Please let me know if you would like some more information about properties/instance variables.

Simon's answer does make a good recommendation about condensing your renderTile: method. Although, I'd do it in a different way. Personally, I like switch statements. You can keep the switch and still condense the code:

-(SKSpriteNode *)renderTile: (int)tileType {
    switch(tileType) {
        case 1:
            // do stuff
            break;
        case 2:
        case 3:
        case 4:
        case 5:
        case 6:
            // do stuff
            break;
        default:
            // do stuff
            break;
    }
}


BUT... if you're doing this, let's use an enum, shall we?

typedef NS_ENUM(unsigned short, MyTileTypeEnum) {
    BlackTile = 1,
    TileTextureA = 2,
    TileTextureB = 3,
    TileTextureC = 4,
    TileTextureD = 5,
    TileTextureE = 6
};


So now our switch can look like this:

switch(tileType) {
    case BlackTile:
        // do stuff
        break;
// ... etc


Of course, the names should be chosen for something that's appropriately descriptive in your case. The point here is we've made our code more self-documenting.

It's important for our code to be easy to read. Code is written for humans and no one likes writing comments, so let's make our code more readable by using good variable names as well as using enums.

Code Snippets

@interface Person : NSObject
@property (nonatomic, strong) NSString *name;
@end
@interface Person : NSObject {
    @private
    NSString *name;
}
- (void)setName:(NSString*)aName;
- (NSString*)name;
@end

@implementation Person
- (void)setName:(NSString*)aName {
    name = aName;
}
- (NSString*)name {
    return name;
}
@interface Person : NSObject
@property NSString *firstName;
@property NSString *lastName;
@property (readonly) NSString *fullName;
@end

@implementation Person
- (NSString*)fullName {
    return [NSString stringWithFormat:"%@ %@", self.firstName, self.lastName];
}
@end
-(SKSpriteNode *)renderTile: (int)tileType {
    switch(tileType) {
        case 1:
            // do stuff
            break;
        case 2:
        case 3:
        case 4:
        case 5:
        case 6:
            // do stuff
            break;
        default:
            // do stuff
            break;
    }
}
typedef NS_ENUM(unsigned short, MyTileTypeEnum) {
    BlackTile = 1,
    TileTextureA = 2,
    TileTextureB = 3,
    TileTextureC = 4,
    TileTextureD = 5,
    TileTextureE = 6
};

Context

StackExchange Code Review Q#40471, answer score: 10

Revisions (0)

No revisions yet.