I started transferring my java knowlege into objective c quite recently and got into making apps with xcode too. I do have a few things I'm confused about though. First of all in java when I'm making top down games and need to shoot a projectile, I would do that somewhat like:
public class Bullet{
int x,y;
public bullet(double x, double y){
this.x = x;
this.y = y;
}
public void tick(){
//logic goes in here to move bullet
}
}
then I'd have a class with an arraylist:
public class MainClass{
ArrayList<Bullet> bulletlist;
public main(){
//create an arraylist that takes Bullet objects
bulletlist = new ArrayList<Bullet>();
//add a new bullet at the coordinate (100,100)
bulletlist.add(new Bullet(100,100));
}
//gameloop(we'll pretend that this gets called every millisecond or so)
public void gameloop(){
//loop through list
for(int i = 0; i < bulletlist.size(); i++){
//run the method tick() at the current index
bulletlist.get(i).tick();
}
}
}
so... my question is how would I translate this code into objective c. Or in other terms, how do I create an arraylist similar to the one in the example that creates an object of a class , then finally loop through this and call a loop method or whatever method I create inside.
Objective-C has no generics, unlike Java. It wouldn't make much sense, since Objective-C is dynamically typed (for the most part). Instead, NSMutableArray and NSArray store instances of NSObject or subtypes thereof (similar to ArrayList<Object> in Java).
Something like this should get you started.
#interface MainClass()
#property(nonatomic, strong) NSMutableArray *bullets;
#end
#implementation MainClass
- (id)init {
if (self = [super init]) {
self.bullets = [NSMutableArray array];
[self.bullets addObject:[[Bullet alloc] initAtX:100 y:100]];
}
return self;
}
- (void)gameLoop {
[self.bullets makeObjectsPerformSelector:#selector(tick)];
}
#end
you create a new file inherited by NSObject with xcode, declare your header and your body, and your ArrayList<Bullet> can be a simple NSMutableArray. Your loop can look like this
NSMutableArray *bulletlist = [[NSMutableArray alloc] init];
// fill the array
for(Bullet *bullet in bulletlist) // or a simple for loop
{
[bullet tick];
}
for steps how to create and declera a class, just check some apple tutorials :)
Your "main class" should be a UIViewController. It should create a UIView which is full screen on the phone. Then it adds a "CALayer" object as a sublayer of that view's layer.
There are various ways to draw into a CALayer, the best in this case would be to create a "BulletLayer" subclass of CALayer and do your drawing in that.
A view is a rectangle of the screen that the user can see and touch. A layer is a set of pixels stored in the video card's [very fast] memory that can be manipulated by your code. For best performance you will only draw the pixels once, and then move them around the screen (which can be done with animation, etc).
If you want specific answers about any particular part of that, you should ask a new question for each after checking if it's already answered somewhere.
It would look like this:
// bullet.h
#interface Bullet : NSObject
#property(assign, nonatomic) CGPoint position;
- (id)initWithPosition:(CGPoint)position;
#end
// bullet.m
#implementation Bullet
- (id)initWithPosition:(CGPoint)position {
self = [self init];
if (self) {
_position = position;
}
return self;
}
#end
Then, in some other class that uses bullets (like a view controller), declare an array to keep the bullets...
#import "Bullet.h"
// put this in the class's interface
#property(nonatomic, strong) NSMutableArray *bullets;
... and add a new bullet like this:
[self.bullets addObject:[[Bullet alloc] initWithPosition:CGPointMake(100,100}];
... and iterate bullets like this:
for (Bullet *bullet in self.bullets) {
NSLog(#"bullet position is %#", NSStringFromCGPoint(bullet.position));
}
Related
I'm having trouble understanding a finer point of Writing and calling Classes. It's probably
easlier to grasp in Swift but it bothers me to start that study without
getting it right in obj_c first. Currently I do everything in the
ViewControllers with iVars and Globals. With two apps 18 months in the App
store its overdue to put them right.
I've formed a notion that properties are the Object's State, and any methods
within determine the Objects Behaviour but so far no-one is able to tell me.
here be a typical Class header:
#interface Math : NSObject
#property (nonatomic, assign) int a;
#property (nonatomic, assign) int b;
#property (nonatomic, assign) int c;
-(int)mathemagic:(int)a adding:(int)b;
#end
and the corresponding Class implementation:
#implementation Math
#synthesize a = _a;
#synthesize b = _b;
#synthesize c = _c;
- (instancetype)init {
self = [super init];
if (self) {
_a = 0;
_b = 0;
_c = 0;
}
return self;
}
-(int)mathemagic:(int)a adding:(int)b {
_c = (a + b);
return _c;
}
#end
and finally in the appropriate places in my ViewController
#import "Math"
- (void)viewDidLoad {
[super viewDidLoad];
Math *theMath = [Math alloc]; // makes no difference if I init[]
theMath.a = 10;
theMath.b = 20;
NSLog (#" answer is %i",theMath.c);
// but still outputs to:
// answer is 0
}
Now I know can make an iVar and do it this way,
int d = [self.theMath mathemagic:theMath.a adding:theMath.b];
NSLog (#" sum: %i",d);
But i shouldn't have to. Stanford CS193P seems to always make the Class a property of the ViewController, but then everything is again expressed as self.theMath.whatever and the Data Model is no longer encapsulated away from the VC ? Maybe Stanford leaves advanced distractions to Java graduates till later.
Well for this person who's read David Flanagan's "Java in A Nutshell" ,
and Niemeyer-Knudsen's "Learning Java", It's later Now.
I shouldn't have to touch theMath.c, just by assigning values to [ theMath.a ] and [ theMath.b ] should be enough.
Where am I wrong?
I think that is because you are setting a and b = 0 in alloc init . and you are not calling [self mathemagic:a adding:b] anywhere.
I think im Math.m you should change -(instancetype)init to
- (instancetype)initWith:(int)a andb:(int)b {
self = [super init];
if (self) {
_c = [self mathemagic:a adding:b];
}
return self;
}
and in viewDidLoad use
Math *theMath = [[Math alloc]initWith:10 andb:20];
Hope this helps :)
I think you have a misconception of how Objective-C classes work.
First of all, it takes two steps to create an object in Objective-C. You must both:
Dynamically allocate memory for the new object
Initialize the newly allocated memory to appropriate values
So your Math instance initialization should look like this:
Math *theMath = [[Math alloc] init];
Just calling alloc zeroes out all instance variables of the object. Although in your case it makes no difference using [Math alloc] or [[Math alloc] init], it's not good programming style.
Second, if by "automatically display behaviour" you mean logging the result of mathemagic:adding: method, then you should pass it as an argument to NSLog function instead of theMath.c
NSLog(#" should show the sum being %i", [theMath mathemagic:theMath.a adding:theMath.b]);
I'm trying to call instances (a random amount of: between 3-7) of an NPC class I created. I have 5 (planning to make more) of such different NPC classes and they all subclass SKSpriteNode. Rough layout of each class is unfortunately simple: a method defining the SKPhysicsBody of the NPC & a few short methods (1 being public) to help with animating the class when importing it into GameScene (SKScene subclass). I wrote A LOT OF CODE in GameScene and 90% of it has to do with how the NPC's behave with the world. The reason all these things are not written inside their respective NPC classes is A)I'm not so good at programming yet and B)most of the code in GameScene relies on the update method:
-(void)update:(NSTimeInterval)currentTime
{
}
Things such as each of the NPC's random movement methods are inside the (void)update method, but these methods are written inside GameScene, not in their respective NPC classes. Because of this, I can't call multiple instances of the same NPC class. If I do, whatever amount I need are created but they're linked to that one random movement method thus overlapping each other while moving in sync making it seem as if it's one NPC unit. So the question: can I just copy NPC methods in GameScene and paste them into their respective class's (obviously working out the kinks) and then just pasting an individual update method into each class? This is so they operate individually when called upon in GameScene and not having to rely on GameScene's (void)update method. If there is some advice, methodology or tutorial on how to go about such things I will be very grateful.
you can use the main update method, and put the update method in your NPC subclasses.
Heres a loose example of how you keep track of time outside of your GameScene
class NPC: SKSpriteNode {
var delta = NSTimeInterval(0)
// this update gets called from the GameScene class,
// put all the NPC movement logic in here
func update(delta: NSTimeInterval){
self.delta = delta
}
}
class GameScene: SKScene {
// time values
var delta:NSTimeInterval = NSTimeInterval(0)
var last_update_time:NSTimeInterval = NSTimeInterval(0)
let NPCs: [NPC] = [NPC(), NPC(), NPC()]
func update(currentTime: NSTimeInterval){
if self.last_update_time == 0.0 || catchUp {
self.delta = 0
} else {
self.delta = currentTime - self.last_update_time
}
self.last_update_time = currentTime
for npc in self.NPCs {
npc.update(delta)
}
}
}
EDIT, THE SAME CODE IN OBJ C
#interface NPC : SKSpriteNode
-(void)update:(NSTimeInterval)delta;
#end
#implementation NPC
NSTimeInterval _delta;
-(void)update:(NSTimeInterval)delta {
_delta = delta;
}
#end
#import "GameScene.h"
#import "NPC.h"
#implementation GameScene
// time values
NSTimeInterval _delta;
NSTimeInterval _last_update_time;
NSMutableArray *_NPCs;
-(void)didMoveToView:(SKView *)view {
_NPCs = [[NSMutableArray alloc]initWithCapacity:3];
for(int i = 0; i < 3; i++){
NPC *npc = [[NPC alloc]init];
[_NPCs addObject:npc];
}
}
-(void)update:(CFTimeInterval)currentTime {
if (_last_update_time) {
_delta = currentTime - _last_update_time;
} else {
_delta = 0;
}
_last_update_time = currentTime;
for(NPC *npc in _NPCs){
[npc update:_delta];
}
}
#end
I am curious how to instead of doing:
- (id)init {
if ((self = [super init])) {
//initialize subclass objects
}
return self;
}
Initialize the subclass by passing a perimeter of the superclass object
- (id)initWithSuperclass:(Superclass *)superclassObject {
if ((self = superclass)) {
//initialize subclass objects
}
return self;
}
Is this possible and if so, is it something that I should avoid? To me, it reeks of smelly code but I don't see another way of passing all the data fields of a superclass object to an initialization of a subclass without knowing every data field in the superclass.
Anyone following my thought/able to help me figure a way through this... smelly code?
Edit:
Ok, so say I have class A and class B below:
#interface A
//stuff but we don't know what
end
#implementation A
- (id)init {
if (self = [super init]) {
//initialize A "stuff"
}
return self;
}
end
#interface B : A
#property (nonatomic, retain) UIButton* one;
- initWithA:(A*)superclassObject;
end
#implementation B
- (id)initWithA:(A*)superclassObject {
if ((self = superclassObject)) {
one = [[UIButton alloc] init];
}
return self;
}
end
The advantage to doing this that I am looking for is that in class A, I do not know all the data fields because it is not my code/code I have access to. I do however need to extend that class. I want to be able to change instances of class A into class B by introducing new data values for class B (like "UIButton one" in the above example) on top of whatever data values/objects may be part of the object instance of class A.
Ignoring obscure advanced features of the runtime, what you are trying to do is not possible in Objective-C - you cannot dynamically extend an object instance from a superclass to a subclass instance adding instance variables in the process.
Think instead of the "has a" model - where B is not a subclass of A but has an instance variable/property of type A.
HTH
Addendum
Seeing your new comments regarding MYButton vs UIButton, why create UIButton instances at all? If you are using Interface Builder/Xcode to generate your UI then you can construct your MYButtons directly - select a UIButton in the designer and change its class to MYButton, see the docs for how, and then you'll create your instances directly.
I have a singleton that I'd like to use to manage the onscreen animation of my views. Here's my.
#import <Foundation/Foundation.h>
#interface OAI_AnimationManager : NSObject {
NSMutableDictionary* sectionData;
}
#property (nonatomic, retain) NSMutableDictionary* sectionData;
+(OAI_AnimationManager* )sharedAnimationManager;
- (void) checkToggleStatus : (UIView* ) thisSection;
#end
.m file
#import "OAI_AnimationManager.h"
#implementation OAI_AnimationManager
#synthesize sectionData;
+(OAI_AnimationManager *)sharedAnimationManager {
static OAI_AnimationManager* sharedAnimationManager;
#synchronized(self) {
if (!sharedAnimationManager)
sharedAnimationManager = [[OAI_AnimationManager alloc] init];
return sharedAnimationManager;
}
}
- (void) checkToggleStatus : (UIView* ) thisSection {
//get the section data dictionary
NSLog(#"%#", sectionData);
}
#end
You'll see in the .h file I added a NSMutableDictionary and am using #property/#synthesize for it's getter and setter.
In my ViewController I instantiate the animation manager as well as a series of subclasses of UIView called Section. With each one I store the data (x/y w/h, title, etc.) in a dictionary and pass that to the dictionary delcared in animation manager. In the Section class I also instantiate animation manager and add a UITapGestureRecognizer which calls a method, which passes along which section was tapped to a method (checkToggleStatus) in animation manager.
As you can I see in the method I am just logging sectionData. Problem is I am getting null for the value.
Maybe my understanding of singletons is wrong. My assumption was the class would only be instantiated once, if it was already instantiated then that existing object would be returned.
I do need all the other Section classes data as if one animates others animate in response and I can get around it by passing the tapped Section to the animation manager and doing [[Section superview] subviews] and then looping and getting the data from each that way but it seems redundant since that data is available in the ViewController when they are created.
Am I doing something wrong in trying to transfer that data? Is there a better solution? I am open to suggestions and criticisms.
Thanks
h file
#interface OAI_AnimationManager : NSObject
#property (nonatomic, retain) NSMutableDictionary* sectionData;
+(OAI_AnimationManager* )sharedAnimationManager;
- (void) checkToggleStatus : (UIView* ) thisSection;
#end
m file
static OAI_AnimationManager* _sharedAnimationManager;
#implementation OAI_AnimationManager
#synthesize sectionData = _sectionData;
+(OAI_AnimationManager *)sharedAnimationManager {
#synchronized(self) {
if (!_sharedAnimationManager) {
_sharedAnimationManager = [[OAI_AnimationManager alloc] init];
}
}
return _sharedAnimationManager;
}
- (void) checkToggleStatus : (UIView* ) thisSection {
//get the section data dictionary
NSLog(#"%#", _sectionData);
}
#end
Notice I moved your sectionData variable from the header and moved it to the implementation file. A while back, they changed it to where you can synthesize properties and specify their instance variable names along side it... hence:
sectionData = _sectionData;
I also added and underscore to the instance variable... this is a universal convention for private variables and it also will throw a compile error now if you try to type just sectionData as you did in the return statement of checkToggleStatus:. Now you either have to type self.sectionData or _sectionData.
You didn't include the code that creates an instance of your dictionary but I bet you didn't set it as self.sectionData = [[NSDictionary alloc] init] which means it would not retain the value and you would get null the next time you called it. Classic memory management mistake... I know it well because I learned the hard way hehehe
I am just trying to make sure I am getting things right as I move forward with Objective-C, two quick questions if I may:
(1) Am I accessing the Position object correctly from within Rectangle? am I right to access the Position object contained within by the pointer I set in the init, or is there a better way?
(2) In [setPosX: andPosY:] Which of the two ways of setting the Position instance variables is best, or does it really not matter?
// INTERFACE
#interface Position: NSObject {
int posX;
int posY;
}
#property(assign) int posX;
#property(assign) int posY;
#end
#interface Rectangle : NSObject {
Position *coord;
}
-(void) setPosX:(int) inPosX andPosY:(int) inPosY;
// IMPLEMENTATION
#implementation Rectangle
-(id) init {
self = [super init];
if (self) {
NSLog(#"_init: %#", self);
coord = [[Position alloc] init];
// Released in dealloc (not shown)
}
return(self);
}
-(void) setPosX:(int) inPosX andPosY:(int) inPosY {
//[coord setPosX:inPosX];
//[coord setPosY:inPosY];
coord.posX = inPosX;
coord.posY = inPosY;
}
EDIT_01
Do I then call -(id)initWithX:andY: from the Rectangle object when I init it? and if so how do I go about setting posX and posY from within main()? or do I replace the init for rectangle with a further -(id)initWithX:andY: and pass the values through?
#implementation Rectangle
-(id) init {
self = [super init];
if (self) {
NSLog(#"_init: %#", self);
coord = [[Position alloc] initWithX:1234 andY:5678];
}
return(self);
}
...
cheers gary
(1) You're accessing it correctly.
(2) In objective-c 2.0, the assignments have identical affect.
Design wise, you would want to make:
-(void) setPosX:(int) inPosX andPosY:(int) inPosY;
...into a method of Position. This encapsulates both the data and the methods related to into one object. So you could have calls like:
coord = [[Position alloc] initWithX:inPosX andY:inPosY];
or
[coord setPosX:inPosX andPosY:inPosY];
all much cleaner and easier to maintain.
Edit O1
Do I then call -(id)initWithX:andY:
from the Rectangle object when I init
it?
That depends on your design. If the coord property is absolutely vital to the Rectangle instance, then you should call it when you initialize a Rectangle instance. You might even write an initializers for Rectangle that takes a position or x and y as input. eg:
-(id) initWithPosition:(Position *) aPos {
self = [super init];
if (self) {
NSLog(#"_init: %#", self);
coord = aPos;
// Released in dealloc (not shown)
}
return self;
}
You should also write a connivence initializer for the Position class:
-(id) initWithX:(NSInteger) x andY:(NSInteger) y{
self=[super init];
self.posX=x;
self.posY=y;
return self;
}
You would then call like:
Position *aPos=[[Position alloc] initWithX:100 andY:50];
Rectangle *aRec=[[Rectangle alloc] initWithPosition:aPos];
Or you could write another combination initializer for Rectangle:
-(id) initWithXCoordinate:(NSInteger) x andYCoordinate:(NSInteger) y{
self=[super init];
Position *aPos=[[Position alloc] initWithX:x andY:y];
self.coord=aPos;
return self;
}
and call it like:
Rectangle *aRec=[[Rectangle alloc] initWithXCoordinate:100
andYCoordinate:50];
These are rough examples but you get the idea. Objective-c gives you a lot of flexibility in setting up initializer so you can create any initializers you find convenient.
You do generally want to avoid using actual functions instead of methods inside classes.
(1) You also need to release it in -dealloc. Does it really make sense to create a Rectangle with an uninitialised position though? In other words, what's the behaviour of [[Position alloc] init], and is it reasonable that a Rectangle should ever have a Position in that state?
(2) They both do the same thing. The one you've written is clearer than the one you've commented out, because it indicates that you're changing properties rather than getting the object to do something. That's my opinion, anyway. Some people agree, others don't, and as I say the behaviour is the same.