From 2c1eaad984f50a816d95521a5838e796f11387e1 Mon Sep 17 00:00:00 2001 From: Dylan Date: Thu, 28 Dec 2023 21:41:54 +0000 Subject: [PATCH] Cleanup init code, infinite gameplay --- src/Models/Alien.zig | 68 +++++--- src/Models/Meteor.zig | 96 ++++++++++- src/Models/Player.zig | 60 ++++++- src/Models/Shoot.zig | 19 +++ src/ViewModels/AsteroidsViewModel.zig | 220 +++++--------------------- 5 files changed, 258 insertions(+), 205 deletions(-) diff --git a/src/Models/Alien.zig b/src/Models/Alien.zig index c42bcc2..f4a334b 100644 --- a/src/Models/Alien.zig +++ b/src/Models/Alien.zig @@ -16,20 +16,67 @@ pub const Alien = struct { shouldDraw: bool = false, const ANIMATION_SPEED_MOD = 10; - pub const ALIEN_SPEED: f32 = 3; + pub const ALIEN_SPEED: f32 = 4; pub const AlienStatusType = enum { shot, - collide, default, }; pub const AlienStatus = union(AlienStatusType) { shot: Shoot, - collide: bool, default: bool, }; + pub inline fn init(player: Player, screenSize: raylib.Vector2, active: bool) Alien { + var alien = Alien{ + .position = raylib.Vector2.init( + 0, + 0, + ), + .speed = raylib.Vector2.init( + 0, + 0, + ), + .radius = 10, + .rotation = Shared.Random.Get().float(f32) * 360, + .active = active, + .color = Shared.Color.Yellow.Base, + .frame = Shared.Random.Get().float(f32) * 10, + }; + + if (active) { + alien.RandomizePosition(player, screenSize, false); + } + + return alien; + } + + pub inline fn RandomizePosition(self: *@This(), player: Player, screenSize: raylib.Vector2, offscreen: bool) void { + var posx: f32 = (Shared.Random.Get().float(f32) * (screenSize.x - 150)) + 150; + while (offscreen) { + const visibleX = posx - player.position.x; + if (visibleX < activeRadiusX or visibleX > -activeRadiusX) { + break; + } + posx = (Shared.Random.Get().float(f32) * (screenSize.x - 150)) + 150; + } + + var posy: f32 = (Shared.Random.Get().float(f32) * (screenSize.y - 150)) + 150; + while (offscreen) { + const visibleY = posy - player.position.y; + if (visibleY < activeRadiusY or visibleY > -activeRadiusY) { + break; + } + posy = (Shared.Random.Get().float(f32) * (screenSize.y - 150)) + 150; + } + + self.position = raylib.Vector2.init( + posx, + posy, + ); + } + pub inline fn Update(self: *@This(), player: Player, comptime shoots: []Shoot, comptime alien_shoots: []Shoot, screenSize: raylib.Vector2) AlienStatus { // If Active if (self.active) { @@ -39,19 +86,6 @@ pub const Alien = struct { self.frame += raylib.getFrameTime() * ANIMATION_SPEED_MOD; } - // Check Collision with player - if (raylib.checkCollisionCircles( - raylib.Vector2.init( - player.collider.x, - player.collider.y, - ), - player.collider.z, - self.position, - self.radius, - )) { - return AlienStatus{ .collide = true }; - } - // Random Motion if (Shared.Random.Get().intRangeAtMost(u8, 0, 10) < 2) { if (self.speed.y == 0) { @@ -106,8 +140,6 @@ pub const Alien = struct { shoots[i].lifeSpawn = 0; self.active = false; - self.color = Shared.Color.Red.Base; - Shared.Sound.Play(.Explosion); return AlienStatus{ .shot = shoots[i] }; diff --git a/src/Models/Meteor.zig b/src/Models/Meteor.zig index e79d9b2..68e771b 100644 --- a/src/Models/Meteor.zig +++ b/src/Models/Meteor.zig @@ -18,6 +18,9 @@ pub const Meteor = struct { frame: f32, const ANIMATION_SPEED_MOD = 15; + pub const METEORS_SPEED = 3; + + const inactivitePoint = -100; pub const MeteorStatusType = enum { shot, @@ -31,11 +34,94 @@ pub const Meteor = struct { default: bool, }; + pub inline fn init(player: Player, screenSize: raylib.Vector2, radius: f32, active: bool) Meteor { + var meteor = Meteor{ + .position = raylib.Vector2.init( + inactivitePoint, + inactivitePoint, + ), + .speed = raylib.Vector2.init( + 0, + 0, + ), + .radius = radius, + .rotation = Shared.Random.Get().float(f32) * 365, + .active = active, + .color = Shared.Color.Blue.Base, + .frame = 0, + }; + if (active) { + meteor.RandomizePositionAndSpeed(player, screenSize, false); + } + + return meteor; + } + + pub inline fn RandomizePositionAndSpeed(self: *@This(), player: Player, screenSize: raylib.Vector2, offscreen: bool) void { + var velx: f32 = undefined; + var vely: f32 = undefined; + + var posx: f32 = (Shared.Random.Get().float(f32) * (screenSize.x - 150)) + 150; + while (offscreen) { + const visibleX = posx - player.position.x; + if (visibleX < activeRadiusX or visibleX > -activeRadiusX) { + break; + } + posx = (Shared.Random.Get().float(f32) * (screenSize.x - 150)) + 150; + } + + var posy: f32 = (Shared.Random.Get().float(f32) * (screenSize.y - 150)) + 150; + while (offscreen) { + const visibleY = posy - player.position.y; + if (visibleY < activeRadiusY or visibleY > -activeRadiusY) { + break; + } + posy = (Shared.Random.Get().float(f32) * (screenSize.y - 150)) + 150; + } + + if (Shared.Random.Get().boolean()) { + velx = Shared.Random.Get().float(f32) * METEORS_SPEED; + } else { + velx = Shared.Random.Get().float(f32) * METEORS_SPEED * -1; + } + if (Shared.Random.Get().boolean()) { + vely = Shared.Random.Get().float(f32) * METEORS_SPEED; + } else { + vely = Shared.Random.Get().float(f32) * METEORS_SPEED * -1; + } + + while (true) { + if (velx == 0 and vely == 0) { + if (Shared.Random.Get().boolean()) { + velx = Shared.Random.Get().float(f32) * METEORS_SPEED; + } else { + velx = Shared.Random.Get().float(f32) * METEORS_SPEED * -1; + } + if (Shared.Random.Get().boolean()) { + vely = Shared.Random.Get().float(f32) * METEORS_SPEED; + } else { + vely = Shared.Random.Get().float(f32) * METEORS_SPEED * -1; + } + } else break; + } + + self.position = raylib.Vector2.init( + posx, + posy, + ); + self.speed = raylib.Vector2.init( + velx, + vely, + ); + } + pub inline fn Update(self: *@This(), player: Player, comptime shoots: []Shoot, comptime aliens: []Alien, comptime alien_shoots: []Shoot, screenSize: raylib.Vector2) MeteorStatus { // If Active if (self.active) { // Reset Frame self.frame = 0; + // Reset color + self.color = Shared.Color.Blue.Base; // Check Collision with player if (raylib.checkCollisionCircles( @@ -148,16 +234,16 @@ pub const Meteor = struct { return MeteorStatus{ .default = true }; } - const screenWidth = 500; - const screenHeight = 325; + const activeRadiusX = 500; + const activeRadiusY = 325; pub inline fn Draw(self: @This(), shipPosition: raylib.Vector2) void { - if (self.position.x == -100 and self.position.y == -100) return; + if (self.position.x == inactivitePoint and self.position.y == inactivitePoint) return; const visibleX = self.position.x - shipPosition.x; const visibleY = self.position.y - shipPosition.y; - if (visibleX > screenWidth or visibleX < -screenWidth) return; - if (visibleY > screenHeight or visibleY < -screenHeight) return; + if (visibleX > activeRadiusX or visibleX < -activeRadiusX) return; + if (visibleY > activeRadiusY or visibleY < -activeRadiusY) return; const spriteFrame = MeteorSprite.getSpriteFrame(@intFromFloat(self.frame)); const color: raylib.Color = if (self.active) self.color else raylib.Color.fade(self.color, 0.3); diff --git a/src/Models/Player.zig b/src/Models/Player.zig index 0d7a738..52072e6 100644 --- a/src/Models/Player.zig +++ b/src/Models/Player.zig @@ -1,7 +1,9 @@ const std = @import("std"); const raylib = @import("raylib"); +const raylib_math = @import("raylib-math"); const Shared = @import("../Shared.zig").Shared; const Shoot = @import("./Shoot.zig").Shoot; +const Alien = @import("./Alien.zig").Alien; pub const Player = struct { position: raylib.Vector2, @@ -25,7 +27,29 @@ pub const Player = struct { default: bool, }; - pub inline fn Update(self: *@This(), comptime shoots: []Shoot, comptime alien_shoots: []Shoot, screenSize: raylib.Vector2, halfShipHeight: f32) PlayerStatus { + pub inline fn init(screenSize: raylib.Vector2, shipHeight: f32) Player { + const position = raylib.Vector2.init( + screenSize.x / 2, + (screenSize.y - shipHeight) / 2, + ); + return Player{ + .position = position, + .speed = raylib.Vector2.init( + 0, + 0, + ), + .acceleration = 0, + .rotation = 0, + .collider = raylib.Vector3.init( + position.x, + position.y, + 12, + ), + .color = Shared.Color.Gray.Light, + }; + } + + pub inline fn Update(self: *@This(), comptime shoots: []Shoot, comptime aliens: []Alien, comptime alien_shoots: []Shoot, screenSize: raylib.Vector2, halfShipHeight: f32) PlayerStatus { // Player logic: rotation if (Shared.Input.Left_Held()) { self.rotation -= 2.5; @@ -111,7 +135,7 @@ pub const Player = struct { // Collision logic: player vs meteors self.collider.x = self.position.x; self.collider.y = self.position.y; - self.collider.z = 12; + //self.collider.z = 12; // Check if alien shot hit inline for (0..alien_shoots.len) |i| { @@ -128,6 +152,38 @@ pub const Player = struct { } } + // Check if alien will collide + inline for (0..aliens.len) |i| { + if (aliens[i].active and raylib.checkCollisionCircles( + aliens[i].position, + aliens[i].radius, + raylib.Vector2.init( + self.position.x - 25, + self.position.y - 25, + ), + self.collider.z + 50, + )) { + aliens[i].rotation = std.math.radiansToDegrees(f32, raylib_math.vector2LineAngle(aliens[i].position, self.position)) - 90; + + aliens[i].position.x = aliens[i].position.x + @sin(std.math.degreesToRadians( + f32, + aliens[i].rotation, + )) * aliens[i].radius; + aliens[i].position.y = aliens[i].position.y - @cos(std.math.degreesToRadians( + f32, + aliens[i].rotation, + )) * aliens[i].radius; + aliens[i].speed.x = @sin(std.math.degreesToRadians( + f32, + aliens[i].rotation, + )) * Alien.ALIEN_SPEED; + aliens[i].speed.y = @cos(std.math.degreesToRadians( + f32, + aliens[i].rotation, + )) * Alien.ALIEN_SPEED; + } + } + return PlayerStatus{ .default = true }; } diff --git a/src/Models/Shoot.zig b/src/Models/Shoot.zig index 366dfd0..555c9bf 100644 --- a/src/Models/Shoot.zig +++ b/src/Models/Shoot.zig @@ -1,4 +1,5 @@ const raylib = @import("raylib"); +const Shared = @import("../Shared.zig").Shared; pub const Shoot = struct { position: raylib.Vector2, @@ -11,6 +12,24 @@ pub const Shoot = struct { const ANIMATION_SPEED_MOD = 30; + pub inline fn init(color: raylib.Color) Shoot { + return Shoot{ + .position = raylib.Vector2.init( + 0, + 0, + ), + .speed = raylib.Vector2.init( + 0, + 0, + ), + .radius = 2, + .rotation = 0, + .active = false, + .lifeSpawn = 0, + .color = color, + }; + } + pub inline fn Update(self: *@This(), screenSize: raylib.Vector2) void { if (self.active) { self.lifeSpawn += raylib.getFrameTime() * ANIMATION_SPEED_MOD; diff --git a/src/ViewModels/AsteroidsViewModel.zig b/src/ViewModels/AsteroidsViewModel.zig index d39cdf3..cd8c6b5 100644 --- a/src/ViewModels/AsteroidsViewModel.zig +++ b/src/ViewModels/AsteroidsViewModel.zig @@ -17,8 +17,6 @@ pub const AsteroidsViewModel = Shared.View.ViewModel.Create( pub const PLAYER_BASE_SIZE: f32 = 20; pub const PLAYER_MAX_SHOOTS: i32 = 10; - const METEORS_SPEED = 3; - pub const MAX_BIG_METEORS = 8; pub const MAX_MEDIUM_METEORS = MAX_BIG_METEORS * 2; pub const MAX_SMALL_METEORS = MAX_MEDIUM_METEORS * 2; @@ -44,8 +42,10 @@ pub const AsteroidsViewModel = Shared.View.ViewModel.Create( pub var aliens: [MAX_ALIENS]Alien = undefined; pub var alien_shoot: [ALIENS_MAX_SHOOTS]Shoot = undefined; + var bigMeteorsCount: u8 = 0; var midMeteorsCount: u8 = 0; var smallMeteorsCount: u16 = 0; + var smallMeteorsDestroyedCount: u2 = 0; pub var score: u64 = 0; @@ -55,10 +55,6 @@ pub const AsteroidsViewModel = Shared.View.ViewModel.Create( pub inline fn init() void { starScape = Starscape.init(screenSize); - var posx: f32 = undefined; - var posy: f32 = undefined; - var velx: f32 = undefined; - var vely: f32 = undefined; shieldLevel = MAX_SHIELD; nextShieldLevel = MAX_SHIELD; @@ -68,193 +64,39 @@ pub const AsteroidsViewModel = Shared.View.ViewModel.Create( )); halfShipHeight = shipHeight / 2; - player = Player{ - .position = raylib.Vector2.init( - screenSize.x / 2, - (screenSize.y - shipHeight) / 2, - ), - .speed = raylib.Vector2.init( - 0, - 0, - ), - .acceleration = 0, - .rotation = 0, - .collider = raylib.Vector3.init( - player.position.x, - player.position.y, - 12, - ), - .color = Shared.Color.Gray.Light, - }; + player = Player.init(screenSize, shipHeight); score = 0; // Initialization shoot for (0..PLAYER_MAX_SHOOTS) |i| { - shoot[i] = Shoot{ - .position = raylib.Vector2.init( - 0, - 0, - ), - .speed = raylib.Vector2.init( - 0, - 0, - ), - .radius = 2, - .rotation = shoot[i].rotation, - .active = false, - .lifeSpawn = 0, - .color = Shared.Color.Tone.Light, - }; + shoot[i] = Shoot.init(Shared.Color.Tone.Light); } // Initialization Big Meteor for (0..MAX_BIG_METEORS) |i| { - posx = Shared.Random.Get().float(f32) * screenSize.x; - while (true) { - if (posx > screenSize.x / 2 - 150 and posx < screenSize.x / 2 + 150) { - posx = Shared.Random.Get().float(f32) * screenSize.x; - } else break; - } - - posy = Shared.Random.Get().float(f32) * screenSize.y; - while (true) { - if (posy > screenSize.y / 2 - 150 and posy < screenSize.y / 2 + 150) { - posy = Shared.Random.Get().float(f32) * screenSize.y; - } else break; - } - - if (Shared.Random.Get().boolean()) { - velx = Shared.Random.Get().float(f32) * METEORS_SPEED; - } else { - velx = Shared.Random.Get().float(f32) * METEORS_SPEED * -1; - } - if (Shared.Random.Get().boolean()) { - vely = Shared.Random.Get().float(f32) * METEORS_SPEED; - } else { - vely = Shared.Random.Get().float(f32) * METEORS_SPEED * -1; - } - - while (true) { - if (velx == 0 and vely == 0) { - if (Shared.Random.Get().boolean()) { - velx = Shared.Random.Get().float(f32) * METEORS_SPEED; - } else { - velx = Shared.Random.Get().float(f32) * METEORS_SPEED * -1; - } - if (Shared.Random.Get().boolean()) { - vely = Shared.Random.Get().float(f32) * METEORS_SPEED; - } else { - vely = Shared.Random.Get().float(f32) * METEORS_SPEED * -1; - } - } else break; - } - - bigMeteors[i] = Meteor{ - .position = raylib.Vector2.init( - posx, - posy, - ), - .speed = raylib.Vector2.init( - velx, - vely, - ), - .radius = 40, - .rotation = Shared.Random.Get().float(f32) * 365, - .active = true, - .color = Shared.Color.Blue.Base, - .frame = 0, - }; + bigMeteors[i] = Meteor.init(player, screenSize, 40, true); + bigMeteorsCount += 1; } // Initialization Medium Meteor for (0..MAX_MEDIUM_METEORS) |i| { - mediumMeteors[i] = Meteor{ - .position = raylib.Vector2.init( - -100, - -100, - ), - .speed = raylib.Vector2.init( - 0, - 0, - ), - .radius = 20, - .rotation = Shared.Random.Get().float(f32), - .active = false, - .color = Shared.Color.Blue.Base, - .frame = 0, - }; + mediumMeteors[i] = Meteor.init(player, screenSize, 20, false); } // Initialization Small Meteor for (0..MAX_SMALL_METEORS) |i| { - smallMeteors[i] = Meteor{ - .position = raylib.Vector2.init( - -100, - -100, - ), - .speed = raylib.Vector2.init( - 0, - 0, - ), - .radius = 10, - .rotation = Shared.Random.Get().float(f32) * 365, - .active = false, - .color = Shared.Color.Blue.Base, - .frame = 0, - }; + smallMeteors[i] = Meteor.init(player, screenSize, 10, false); } // Initialization Aliens for (0..MAX_ALIENS) |i| { - posx = Shared.Random.Get().float(f32) * screenSize.x; - while (true) { - if (posx > screenSize.x / 2 - 150 and posx < screenSize.x / 2 + 150) { - posx = Shared.Random.Get().float(f32) * screenSize.x; - } else break; - } - - posy = Shared.Random.Get().float(f32) * screenSize.y; - while (true) { - if (posy > screenSize.y / 2 - 150 and posy < screenSize.y / 2 + 150) { - posy = Shared.Random.Get().float(f32) * screenSize.y; - } else break; - } - - aliens[i] = Alien{ - .position = raylib.Vector2.init( - posx, - posy, - ), - .speed = raylib.Vector2.init( - 0, - 0, - ), - .radius = 10, - .rotation = Shared.Random.Get().float(f32) * 360, - .active = true, - .color = Shared.Color.Yellow.Base, - .frame = Shared.Random.Get().float(f32) * 10, - }; + aliens[i] = Alien.init(player, screenSize, true); } // Initialization alien shoot for (0..ALIENS_MAX_SHOOTS) |i| { - alien_shoot[i] = Shoot{ - .position = raylib.Vector2.init( - 0, - 0, - ), - .speed = raylib.Vector2.init( - 0, - 0, - ), - .radius = 2, - .rotation = shoot[i].rotation, - .active = false, - .lifeSpawn = 0, - .color = Shared.Color.Green.Light, - }; + alien_shoot[i] = Shoot.init(Shared.Color.Green.Light); } midMeteorsCount = 0; @@ -276,7 +118,7 @@ pub const AsteroidsViewModel = Shared.View.ViewModel.Create( } // Update Player - switch (player.Update(&shoot, &alien_shoot, screenSize, halfShipHeight)) { + switch (player.Update(&shoot, &aliens, &alien_shoot, screenSize, halfShipHeight)) { .collide => { shieldLevel = 0; }, @@ -289,11 +131,12 @@ pub const AsteroidsViewModel = Shared.View.ViewModel.Create( // Update Aliens inline for (0..MAX_ALIENS) |i| { switch (aliens[i].Update(player, &shoot, &alien_shoot, screenSize)) { - .collide => { - shieldLevel = 0; - }, .shot => { score += 8; + + // Move to new random position and reactivate + aliens[i].RandomizePosition(player, screenSize, false); + aliens[i].active = true; }, .default => {}, } @@ -317,6 +160,7 @@ pub const AsteroidsViewModel = Shared.View.ViewModel.Create( switch (bigMeteors[i].Update(player, &shoot, &aliens, &alien_shoot, screenSize)) { .default => {}, .shot => |shot| { + bigMeteorsCount -= 1; score += 4; for (0..2) |_| { @@ -327,13 +171,13 @@ pub const AsteroidsViewModel = Shared.View.ViewModel.Create( if (@rem(midMeteorsCount, 2) == 0) { mediumMeteors[@intCast(midMeteorsCount)].speed = raylib.Vector2.init( - @cos(std.math.degreesToRadians(f32, shot.rotation)) * METEORS_SPEED * -1, - @sin(std.math.degreesToRadians(f32, shot.rotation)) * METEORS_SPEED * -1, + @cos(std.math.degreesToRadians(f32, shot.rotation)) * Meteor.METEORS_SPEED * -1, + @sin(std.math.degreesToRadians(f32, shot.rotation)) * Meteor.METEORS_SPEED * -1, ); } else { mediumMeteors[@intCast(midMeteorsCount)].speed = raylib.Vector2.init( - @cos(std.math.degreesToRadians(f32, shot.rotation)) * METEORS_SPEED, - @sin(std.math.degreesToRadians(f32, shot.rotation)) * METEORS_SPEED, + @cos(std.math.degreesToRadians(f32, shot.rotation)) * Meteor.METEORS_SPEED, + @sin(std.math.degreesToRadians(f32, shot.rotation)) * Meteor.METEORS_SPEED, ); } @@ -352,6 +196,7 @@ pub const AsteroidsViewModel = Shared.View.ViewModel.Create( switch (mediumMeteors[i].Update(player, &shoot, &aliens, &alien_shoot, screenSize)) { .default => {}, .shot => |shot| { + midMeteorsCount -= 1; score += 2; for (0..2) |_| { @@ -362,13 +207,13 @@ pub const AsteroidsViewModel = Shared.View.ViewModel.Create( if (@rem(smallMeteorsCount, 2) == 0) { smallMeteors[@intCast(smallMeteorsCount)].speed = raylib.Vector2.init( - @cos(std.math.degreesToRadians(f32, shot.rotation)) * METEORS_SPEED * -1, - @sin(std.math.degreesToRadians(f32, shot.rotation)) * METEORS_SPEED * -1, + @cos(std.math.degreesToRadians(f32, shot.rotation)) * Meteor.METEORS_SPEED * -1, + @sin(std.math.degreesToRadians(f32, shot.rotation)) * Meteor.METEORS_SPEED * -1, ); } else { smallMeteors[@intCast(smallMeteorsCount)].speed = raylib.Vector2.init( - @cos(std.math.degreesToRadians(f32, shot.rotation)) * METEORS_SPEED, - @sin(std.math.degreesToRadians(f32, shot.rotation)) * METEORS_SPEED, + @cos(std.math.degreesToRadians(f32, shot.rotation)) * Meteor.METEORS_SPEED, + @sin(std.math.degreesToRadians(f32, shot.rotation)) * Meteor.METEORS_SPEED, ); } @@ -386,13 +231,28 @@ pub const AsteroidsViewModel = Shared.View.ViewModel.Create( switch (smallMeteors[i].Update(player, &shoot, &aliens, &alien_shoot, screenSize)) { .default => {}, .shot => { + smallMeteorsCount -= 1; score += 1; + + // After 4 small meteors are destroyed, create a new big one + if (smallMeteorsDestroyedCount == 3) { + bigMeteors[@intCast(bigMeteorsCount)].RandomizePositionAndSpeed(player, screenSize, true); + bigMeteors[@intCast(bigMeteorsCount)].active = true; + + smallMeteorsDestroyedCount = 0; + bigMeteorsCount += 1; + Shared.Log.Info("New Big Meteor"); + } + smallMeteorsDestroyedCount += 1; }, .collide => { shieldLevel = 0; }, } } + + // Uncomment to disable gameover + //shieldLevel = MAX_SHIELD; } }, .{