Hello, Habr! At one time the articles "here is my first game" were very popular here. Recently, I have not seen them, so I decided to fill this gap myself.
History of creation and gameplay
I wanted to make a game created independently from start to finish for a long time - I started back in 2012 with a top-down shooter on java for android. Not quickly realizing that it is impossible to pull it alone, after a couple of years I switched to a miniature strategy in C ++. After a year or so, interest in her also disappeared, and although I sometimes returned to these projects, I seriously realized that I would never finish them. And then I read a lot of articles for newcomers to gamedev and decided to take the path of least resistance: quickly clone something known. Only such a path would allow at last to bring at least something to a logical end.
The game Threes was taken as a basis, or rather, its clone 2048, into which I was then very stuck. As an original feature, it was decided to make the field not square, but hexagonal. And also with the ability to choose its size. And to make it completely unlike either Threes or 2048, make the principle of combining cells dynamic - the user is free to choose whether he likes to drive triples or powers of two.
, , , . : 33, 77 (, , , , ). , scrum- . : 1, 2, 3, 5, 8 ( , ).
" !", , . : 1 "" 2, . 8 , .
, . ", ?" - . .
, ? , ! , ( ). , . : , , . , , . , , .
, . , Model - ViewController - Command, , - , . , . swift C# . , , , , , , . , Swift , , , .
, . , , motion blur, haptic feedback, , , , , ( ), app store , -, ffmpeg' , privacy policy, , - , -, , . , 238 . , , .
, ( , ). , . , .
- , .
, , .
, , qBasic. : , , - . , , . , . : XUp, XDown, YUp, YDown, Left and Right, , . :
class BaseCellsIterator {
internal var line = LineCellsContainer() //
internal var x: Int = 0
internal var y: Int = 0
internal var w: Int { self.gameModel.field.width } //
internal var h: Int { self.gameModel.field.height } //
}
class MoveXDownIterator: BaseCellsIterator, CellsIterator {
func next() -> LineCellsContainer? {
line.clear() //
if x >= w { // "" ,
x = 0
y += 1
}
if y >= h { // "" , ,
return nil
}
// for(; x <= w; x++) ""
for _ in x ..< w {
defer { x += 1 }
guard let cell = getCell(x, y),
!cell.isBlocked,
!cell.isBlockedFromSwipe
else { break } // ,
line.add(cell)
}
return line
}
}
. , - , . , , - ( , placeholder'). , , , . .
- , - . , SpriteKit , , , , . SKNode , ( - , - , ), :
public func renderNode(node: SKNode, filename: String) throws {
let destinationURL = URL(fileURLWithPath: filename, isDirectory: false) as CFURL
guard let texture = view.texture(from: node) else { throw ImageGeneratorError.textureRenderFailed }
let image = texture.cgImage()
// "public.png" Uniform Type Identifier, , , .
guard let destination = CGImageDestinationCreateWithURL(destinationURL, "public.png" as CFString, 1, nil) else { throw ImageGeneratorError.destinationCreationFailed }
let imageProperties = [kCGImageDestinationLossyCompressionQuality as String: 0.8]
CGImageDestinationAddImage(destination, image,imageProperties as CFDictionary)
let result = CGImageDestinationFinalize(destination)
let date = Date()
let calendar = Calendar.current
let hour = calendar.component(.hour, from: date)
let minutes = calendar.component(.minute, from: date)
let time = "\(hour):\(minutes)"
print(result
? "\(filename) rendered successfully at \(time)"
: "\(filename) render FAILED at \(time)")
}
: , - , . , .
- - App Store, , . - 37 , 6 . . -, .
, , , - , , - - . , , . - , - . .
:
.
. , Swift #8/9.
. , , . (" ") .
, full-stack , ios-. , , , " .net, Swift" .
And the most important result is that now I finally have a game that I personally would like to play on the train on my way home from work. It's a pity, I've been working from home for a year now.