diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3b29812 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +.DS_Store +/.build +/Packages +/*.xcodeproj +xcuserdata/ +DerivedData/ +.swiftpm/config/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..0da90a5 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "type": "lldb", + "request": "launch", + "name": "Debug Snake", + "program": "${workspaceFolder:snake}/.build/debug/Snake", + "args": [], + "cwd": "${workspaceFolder:snake}", + "preLaunchTask": "swift: Build Debug Snake" + }, + { + "type": "lldb", + "request": "launch", + "name": "Release Snake", + "program": "${workspaceFolder:snake}/.build/release/Snake", + "args": [], + "cwd": "${workspaceFolder:snake}", + "preLaunchTask": "swift: Build Release Snake" + } + ] +} \ No newline at end of file diff --git a/Package.resolved b/Package.resolved new file mode 100644 index 0000000..2ee218e --- /dev/null +++ b/Package.resolved @@ -0,0 +1,14 @@ +{ + "pins" : [ + { + "identity" : "raylib", + "kind" : "remoteSourceControl", + "location" : "https://github.com/STREGAsGate/Raylib.git", + "state" : { + "branch" : "master", + "revision" : "e7cbfd48d26f85994fecc85881b4f38ca23516a3" + } + } + ], + "version" : 2 +} diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..b20e3ee --- /dev/null +++ b/Package.swift @@ -0,0 +1,20 @@ +// swift-tools-version: 5.6 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "Snake", + dependencies: [ + // Dependencies declare other packages that this package depends on. + .package(url: "https://github.com/STREGAsGate/Raylib.git", branch: "master") + ], + targets: [ + // Targets are the basic building blocks of a package. A target can define a module or a test suite. + // Targets can depend on other targets in this package, and on products in packages this package depends on. + .executableTarget( + name: "Snake", + dependencies: ["Raylib"] + ) + ] +) diff --git a/README.md b/README.md new file mode 100644 index 0000000..c4b3cf7 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# Snake + +A description of this package. diff --git a/Sources/Snake/Game.swift b/Sources/Snake/Game.swift new file mode 100644 index 0000000..1aa634e --- /dev/null +++ b/Sources/Snake/Game.swift @@ -0,0 +1,324 @@ +// +// Snake +// + +import Raylib + +class Game { + // 16:9 + // private let windowWidth: Int32 = 640 + // private let windowHeight: Int32 = 360 + + // 16:10 + private let windowWidth: Int32 = 640 + private let windowHeight: Int32 = 400 + + // 21:9 + // private let windowWidth: Int32 = 840 + // private let windowHeight: Int32 = 360 + + private let fieldSquareSize: Int32 = 20 + private let fieldWidth: Int32 + private let fieldHeight: Int32 + private let fieldRender: RenderTexture2D + // private let fieldTexture: Texture2D + private let fieldPositionDraw: Vector2 + + struct Position2D { + var x: Int32, y: Int32 + } + + struct SnakeHead { + var position: Vector2 + + enum Movement { + case up, down, left, right + } + + var movement: Movement + var previousPosition: Position2D { + var position = Position2D(x: Int32(position.x), y: Int32(position.y)) + switch movement { + case .up: + position.y += 1 + case .down: + position.y -= 1 + case .left: + position.x += 1 + case .right: + position.x -= 1 + } + return position + } + let speed: Float32 + } + + private var snakeHead = SnakeHead(position: Vector2(x: 3, y: 3), movement: .right, speed: 12.5) + private var snakeTail = [Position2D]() + + private var fruitPosition: Position2D + // Position2D(x: Int32.random(in: 0...fieldWidth), y: Int32.random(in: 0...fieldHeight)) + + private var isPause = false + + init() { + Raylib.setConfigFlags(.vsyncHint) + Raylib.initWindow(windowWidth, windowHeight, "Snake") + // Raylib.setWindowPosition(364, 364) + Raylib.setExitKey(.letterQ) + + fieldWidth = windowWidth / fieldSquareSize + fieldHeight = windowHeight / fieldSquareSize + + fieldRender = Raylib.loadRenderTexture( + fieldWidth * fieldSquareSize, fieldHeight * fieldSquareSize) + + Raylib.beginTextureMode(fieldRender) + Raylib.clearBackground(.lightGray) + + for positionX in 0..= fieldWidth || y < 0 || y >= fieldHeight { + // isPause = true + // } + + if snakeHead.position.x < 0 { + snakeHead.position.x += Float(fieldWidth) + } else if snakeHead.position.x > Float(fieldWidth) { + snakeHead.position.x -= Float(fieldWidth) + } + if snakeHead.position.y < 0 { + snakeHead.position.y += Float(fieldHeight) + } else if snakeHead.position.y > Float(fieldHeight) { + snakeHead.position.y -= Float(fieldHeight) + } + + let previousPosition = Position2D( + x: Int32(snakeHead.position.x), y: Int32(snakeHead.position.y)) + + switch snakeHead.movement { + case .up: + snakeHead.position.y -= snakeHead.speed * deltaTime + case .down: + snakeHead.position.y += snakeHead.speed * deltaTime + case .left: + snakeHead.position.x -= snakeHead.speed * deltaTime + case .right: + snakeHead.position.x += snakeHead.speed * deltaTime + } + + if Int32(snakeHead.position.x) != previousPosition.x + || Int32(snakeHead.position.y) != previousPosition.y + { + snakeTail.append(snakeHead.previousPosition) + // snakeTail.append(Position2D(x: Int32(snakeHead.position.x), y: Int32(snakeHead.position.y))) + snakeTail.removeFirst() + } + + if Int32(snakeHead.position.x) == fruitPosition.x + && Int32(snakeHead.position.y) == fruitPosition.y + { + snakeTail.append(Position2D(x: Int32(snakeHead.position.x), y: Int32(snakeHead.position.y))) + + fruitPosition = Position2D( + x: Int32.random(in: 0.. 4 && tailItem.x != snakeTail.first?.x && tailItem.y != snakeTail.first?.y + // { + // if Int32(snakeHead.position.x) == tailItem.x && Int32(snakeHead.position.y) == tailItem.y { + // isPause = true + // } + // } + } + + private func render() { + // Raylib.beginTextureMode(fieldRender) + // Raylib.clearBackground(.black) + + // // Raylib.drawTextureV(fieldTexture, Vector2(), .white) + // // Raylib.drawTexture(fieldTexture, 0, 0, .blue) + // Raylib.drawRectangle(1, 1, 30, 30, .white) + // // Draw snake head. + // Raylib.drawRectangle( + // snakeHead.position.x * fieldSquareSize, snakeHead.position.y * fieldSquareSize, + // fieldSquareSize, fieldSquareSize, .magenta) + + // Raylib.endTextureMode() + + Raylib.beginDrawing() + Raylib.clearBackground(.white) + + // Raylib.drawRectangleGradientH(0, 0, windowWidth, windowHeight, .magenta, .black) + // Raylib.drawCircleGradient(windowWidth / 2, windowHeight / 2, Float(windowWidth), .black, .magenta) + + // Raylib.drawRectangleLines( + // Int32(fieldPositionDraw.x - 1), Int32(fieldPositionDraw.y - 1), + // fieldWidth * fieldSquareSize + 2, + // fieldHeight * fieldSquareSize + 2, .lightGray) + + // Draw field border. + // Raylib.drawRectangleLines( + // Int32(fieldPositionDraw.x - 1), Int32(fieldPositionDraw.y - 1), + // fieldWidth * fieldSquareSize + 2, + // fieldHeight * fieldSquareSize + 2, .lightGray) + // Draw field. + Raylib.drawTextureV(fieldRender.texture, fieldPositionDraw, .white) + + // Draw snake tail. + for snakeTailItem in snakeTail { + Raylib.drawRectangleRec( + Rectangle( + x: Float(snakeTailItem.x) * Float(fieldSquareSize) + fieldPositionDraw.x, + y: Float(snakeTailItem.y) * Float(fieldSquareSize) + fieldPositionDraw.y, + width: Float(fieldSquareSize), + height: Float(fieldSquareSize) + ), .purple) + } + // Draw snake head. + Raylib.drawRectangleRec( + Rectangle( + x: Float(Int32(snakeHead.position.x)) * Float(fieldSquareSize) + fieldPositionDraw.x, + y: Float(Int32(snakeHead.position.y)) * Float(fieldSquareSize) + fieldPositionDraw.y, + width: Float(fieldSquareSize), + height: Float(fieldSquareSize) + ), .darkPurple) + // Draw fruit. + Raylib.drawRectangleRec( + Rectangle( + x: Float(fruitPosition.x) * Float(fieldSquareSize) + fieldPositionDraw.x, + y: Float(fruitPosition.y) * Float(fieldSquareSize) + fieldPositionDraw.y, + width: Float(fieldSquareSize), height: Float(fieldSquareSize) + ), .red) + + if isPause { + Raylib.drawRectangleRec( + Rectangle(x: 0, y: 0, width: Float(windowWidth), height: Float(windowHeight)), + // Color(r: 0, g: 0, b: 0, a: 128) + Color(r: 255, g: 255, b: 255, a: 128) + // Color(r: 230, g: 41, b: 55, a: 128) + // Color(r: 0, g: 121, b: 241, a: 128) + ) + + let text = "Pause" + let fontSize: Int32 = 30 + let positionDraw = Position2D( + x: (windowWidth - Raylib.measureText(text, fontSize)) / 2, y: (windowHeight - fontSize) / 2) + + // Draw shadow. + // Raylib.drawText(text, positionDraw.x + 1, positionDraw.y + 1, fontSize, .black) + // Draw text. + Raylib.drawText(text, positionDraw.x, positionDraw.y, fontSize, .black) + } + + Raylib.drawFPS(8, 8) + Raylib.drawText( + """ + Snake head: + Position: \(Int32(snakeHead.position.x)), \(Int32(snakeHead.position.y)) + Speed: \(snakeHead.speed) + + Snake tail: + Count: \(snakeTail.count) + """, 8, 36, 10, .black) + + Raylib.endDrawing() + } + + func run() { + while !Raylib.windowShouldClose { + input() + update(deltaTime: Raylib.getFrameTime()) + render() + } + } + + deinit { + Raylib.closeWindow() + } +} diff --git a/Sources/Snake/main.swift b/Sources/Snake/main.swift new file mode 100644 index 0000000..ea04fcb --- /dev/null +++ b/Sources/Snake/main.swift @@ -0,0 +1,6 @@ +// +// Snake +// + +let game = Game() +game.run()