OC version: Snake, oc snake
Yesterday I wrote a js version of Snake. Today I suddenly want to write an OC version to compare the differences between the two languages.
The oc feature is applicable to all iphone sizes and can be paused. You can set the ratio of map to snake to accelerate.
The comparison will show that the js version is simpler than the OC version. If you want to see the js version, you can see my previous article.
No material is used in the program, as follows:
Github Source Code address: https://github.com/masterChunlinHan/snake_OC
At the beginning, just like the js version, all the code is written in a controller for easy learning, so there is no need to write anything in the header file.
#import <UIKit/UIKit.h>@interface ViewController : UIViewController@end
1. First, let's take a look at the macros to be defined and the required global variables. The meanings of each variable have been written in comments.
# Import "ViewController. h "# define SCREENWIDTH [UIScreen mainScreen]. bounds. size. width // screen height # define SCREENHEIGHT [UIScreen mainScreen]. bounds. size. height // screen width # define LINECOUNT 30 // number of rows displayed on the map, which indicates that the map is a square, therefore, you only need a number # define boxwidth screenheight/LINECOUNT // The width of each grid, which is both the width of the food and the width of each snake body @ interface ViewController () @ property (nonatomic, strong) UIView * map; // there is only one map, which is defined as global @ property (nonatomic, strong) UIView * food; // there is only one @ property (nonatomic, assign) for food) NSInteger foodx; // x coordinate of the food, in the unit of grid width @ property (nonatomic, assign) NSInteger foody; // y coordinate of the food @ property (nonatomic, strong) NSMutableArray * body; // the body of the snake changes, so a variable array is used to store @ property (nonatomic, strong) NSTimer * timer; // timer @ property (nonatomic, assign) NSInteger directionCode; // defines a number to indicate the direction 0, 1, and 2, respectively, representing the upper, lower, and lower @ property (nonatomic, assign) float time; // The timer interval, the default value is 0.5 seconds.
2. Some initialization work
@ Implementation ViewController // initialization timer interval-(float) time {if (! _ Time) {_ time = 0.5;} return _ time;} // create some unchanged controls in viewDidLoad. These controls always have only one copy, you do not need to create the second-(void) viewDidLoad {[super viewDidLoad]; // create a map [self createMap]; // Add the reset button. By default, the game will not start immediately when you enter the program, you need to click this button to start [self createReloadButton]; // Add the start pause button [self createControlButtons]; // Add the direction key [self createdirebuttons];}
3. Create a map
-(Void) createMap {// first, judge whether the map already exists. if so, if (! Self. map) {self. map = [[UIView alloc] initWithFrame: CGRectMake (0, 0, SCREENHEIGHT, SCREENHEIGHT)]; [self. view addSubview: self. map]; UIColor * lightColor = [UIColor colorWithRed: 238/255. 0 green: 238/255. 0 blue: 238/255. 0 alpha: 1]; self. map. backgroundColor = lightColor; self. map. center = self. view. center ;}}
4. Create food
// Create a food. This method is called every time you refresh it-(void) createFood {// first checks whether the food already exists. if so, if (! Self. food) {self. food = [[UIView alloc] initWithFrame: CGRectMake (0, 0, BOXWIDTH, BOXWIDTH)]; UIColor * foodColor = [UIColor redColor]; self. food. backgroundColor = foodColor; [self. map addSubview: self. food];} // The coordinates of each food refresh are a random value self. foodx = arc4random () % LINECOUNT; self. foody = arc4random () % LINECOUNT; self. food. frame = CGRectMake (self. foodx * BOXWIDTH, self. foody * BOXWIDTH, BOXWIDTH, BOXWIDTH );}
5. Create a snake
// Create a snake. Change the body coordinate of the snake every time you refresh it, and then call this method to re-display the snake-(void) createSnake {// first determine whether the food already exists, if so, if (! Self. body) {// initialize the forward direction of the snake to the right self. direcode code = 3; // initialize the snake's body. At the beginning, there is only one head, two bodies, the head is red, and the body is blue self. body = [NSMutableArray arrayWithArray: @ [@ {@ "bodyx": @ "2", @ "bodyy": @ "2", @ "color ": [UIColor redColor]}, @ {@ "bodyx": @ "1", @ "bodyy": @ "2", @ "color": [UIColor blueColor]}, @ {@ "bodyx": @ "0", @ "bodyy": @ "2", @ "color": [UIColor blueColor]}];} for (int I = 0; I <self. body. count; I ++) {NSMutableDictionary * mDic = [NSMutableDict Ionary dictionaryWithDictionary: self. body [I]; UIView * body = mDic [@ "body"]; if (! Body) {body = [[UIView alloc] initWithFrame: CGRectMake (0, 0, BOXWIDTH, BOXWIDTH)]; [self. map addSubview: body]; [mDic setObject: body forKey: @ "body"];} float bodyx = BOXWIDTH * [mDic [@ "bodyx"] intValue]; float bodyy = BOXWIDTH * [mDic [@ "bodyy"] intValue]; body. frame = CGRectMake (bodyx, bodyy, BOXWIDTH, BOXWIDTH); body. backgroundColor = mDic [@ "color"]; self. body [I] = mDic ;}}
6. Snake movement Method
// When the snake moves, the coordinates of the snake will be changed in this method each time it is refreshed, And the collision with the food and its own-(void) snake move {// set the snake body for (NSInteger I = self. body. count-1; I> 0; I --) {NSMutableDictionary * mDic = [NSMutableDictionary dictionaryWithDictionary: self. body [I]; NSString * bodyx = self. body [I-1] [@ "bodyx"]; NSString * bodyy = self. body [I-1] [@ "bodyy"]; [mDic setObject: bodyx forKey: @ "bodyx"]; [mDic setObject: bodyy forKey: @ "bodyy"]; self. body [I] = mDic;} // sets the NSMutableDictionary * mDic = [NSMutableDictionary dictionaryWithDictionary: self. body [0]; NSString * headx = mDic [@ "bodyx"]; NSString * heady = mDic [@ "bodyy"]; if (self. direcode code = 0) {// heady = [NSString stringWithFormat: @ "% d", [heady intValue]-1];} else if (self. direcode code = 1) {// heady = [NSString stringWithFormat: @ "% d", [heady intValue] + 1];} else if (self. direcode code = 2) {// left headx = [NSString stringWithFormat: @ "% d", [headx intValue]-1];} else if (self. direcode code = 3) {// right headx = [NSString stringWithFormat: @ "% d", [headx intValue] + 1];} [mDic setObject: headx forKey: @ "bodyx"]; [mDic setObject: heady forKey: @ "bodyy"]; self. body [0] = mDic; // determines whether the food is eaten. if ([headx integerValue] = self. foodx & [heady integerValue] = self. foody) {[self. body addObject: @ {@ "bodyx": @ "-1", @ "bodyy": @ "0", @ "color": [UIColor blueColor]}]; if (self. time> 0.25) {self. time-= 0.05; [self start]; // [self createTimer];} [self createFood];} // determine whether the boundary is hit if ([headx integerValue] <0 | [headx integerValue]> LINECOUNT-1 | [heady intValue] <0 | [heady intValue]> LINECOUNT-1) {// [self reload]; [self stop]; [self gameOver]; return;} // determines whether a user hits [self createSnake];}
7. Create a timer. Because the timer creation function is the same as the method used to resume the game after it is paused, you can directly call the method used to continue the game.
// Create a timer-(void) createTimer {[self start];}
Continue the game
// Continue. Destroy the old timer before creating the timer here to ensure that there is always only one timer at work-(void) start {// if you do not destroy the timer first, clicking the "continue" button will create many timers, And the bug that snakes run fast will appear. [self. timer invalidate]; self. timer = [nst1_timerwithtimeinterval: self. time repeats: YES block: ^ (NSTimer * _ Nonnull timer) {[self snkemove] ;}]; [[nsunloop currentRunLoop] addTimer: self. timer forMode: NSDefaultRunLoopMode];}
8. Pause the game. Just destroy the timer.
// Pause-(void) stop {[self. timer invalidate];}
9. Set the reset button method. Each execution (including the first time) clears the map and creates a snake, food, and resets the timer.
// Reload method, reset all items after execution, remove all items on the map, and recreate-(void) reload {self. body = nil; for (UIView * subview in self. map. subviews) {if (subview! = Self. food) {[subview removeFromSuperview] ;}} [self createFood]; [self createSnake]; [self createTimer] ;}// create reset button-(void) createReloadButton {UIButton * reloadBtn = [[UIButton alloc] initWithFrame: CGRectMake (0, 0, (SCREENWIDTH-SCREENHEIGHT)/2, SCREENHEIGHT/3)]; [self. view addSubview: reloadBtn]; [reloadBtn setTitle: @ "Reset/start" forState: UIControlStateNormal]; reloadBtn. backgroundColor = [UIColor orangeColor]; [reloadBtn addTarget: self action: @ selector (reload) forControlEvents: UIControlEventTouchUpInside];}
10. create various buttons, which can be called after the map is created, because some buttons are placed relative to the map location.
// The following is to create a variety of buttons, not to mention the // up and down buttons-(void) createdirebutbuttons {// float btnW = (SCREENWIDTH-SCREENHEIGHT)/2; // The left direction key view UIView * leftDirectionView = [[UIView alloc] initWithFrame: CGRectMake (0, SCREENHEIGHT/3, (SCREENWIDTH-SCREENHEIGHT)/2, 2 * SCREENHEIGHT/3)]; [self. view addSubview: leftdireview view]; // view UIView * rightDirectionView = [[UIView alloc] initWithFrame: CGRectMake (CGRectGetMaxX (self. map. frame), SCREENHEIGHT/3, (SCREENWIDTH-SCREENHEIGHT)/2, 2 * SCREENHEIGHT/3)]; [self. view addSubview: rightDirectionView]; float btnH = 2 * SCREENHEIGHT/9; for (int I = 0; I <4; I ++) {NSString * title; float x; float y; float w; UIColor * backColor; switch (I) {case 0: x = 0; y = 0; w = (SCREENWIDTH-SCREENHEIGHT)/2; title = @ "TOP"; backColor = [UIColor redColor]; break; case 1: x = 0; y = 2 * btnH; w = (SCREENWIDTH-SCREENHEIGHT)/2; title = @ ""; backColor = [UIColor greenColor]; break; case 2: x = 0; y = btnH; w = (SCREENWIDTH-SCREENHEIGHT)/4; title = @ "Left"; backColor = [UIColor grayColor]; break; default: x = (SCREENWIDTH-SCREENHEIGHT)/4; y = btnH; w = (SCREENWIDTH-SCREENHEIGHT)/4; title = @ "right"; backColor = [UIColor orangeColor]; break;} // The left button UIButton * btnLeft = [[UIButton alloc] initWithFrame: CGRectMake (x, y, w, btnH)]; btnLeft. tag = I; [btnLeft setBackgroundColor: backColor]; [btnLeft setTitle: title forState: UIControlStateNormal]; [btnLeft addTarget: self action: @ selector (directionChanged :) forControlEvents: callback]; // The right button UIButton * btnRight = [[UIButton alloc] initWithFrame: CGRectMake (x, y, w, btnH)]; btnRight. tag = I; [btnRight setBackgroundColor: backColor]; [btnRight setTitle: title forState: UIControlStateNormal]; [btnRight addTarget: self action: @ selector (directionChanged :) forControlEvents: callback]; [leftDirectionView addSubview: btnLeft]; [rightDirectionView addSubview: btnRight]; // [self. view addSubview: btn] ;}// click the button to change the global variable that represents the direction. In this way, you can get the-(void) in the method of snake movement) direchanged changed :( UIButton *) btn {self. direcode code = btn. tag; // NSLog (@ "% zd", btn. tag) ;}@ end