mirror of
https://github.com/italicsjenga/agb.git
synced 2024-12-24 00:31:34 +11:00
Purple night fixes (#415)
A few changes to the purple night to make it so much better! * Fixes player positioning to be in the centre, no more drastic change of position when changing directions, and you don't go all the way into walls. * Fixes rounding of collision to round rather than floor, this means the left and right walls are more equivalent. * Fixes the "jitter" between objects and backgrounds where objects would move by a pixel, but the background wouldn't. - In general we're more careful to do all display changes at once rather than throughout the frame. - Note that for future games this is the correct way of doing things. * Fixes slime collision boxes to match the extent of the animation. * Use more i32 rather than u16, we're not running out of memory! * Fixes memory leak caused by dying (previously you could only die a certain number of times before the game crashes). * Moved sound to be during vblank - This is a negative change but resolves some cracking issues. I can't see why this should be the case, further work may be required here. - [x] No changelog update needed
This commit is contained in:
commit
542e683dc8
|
@ -21,7 +21,7 @@ use agb::{
|
||||||
},
|
},
|
||||||
Priority, HEIGHT, WIDTH,
|
Priority, HEIGHT, WIDTH,
|
||||||
},
|
},
|
||||||
fixnum::{FixedNum, Rect, Vector2D},
|
fixnum::{num, FixedNum, Rect, Vector2D},
|
||||||
input::{Button, ButtonController, Tri},
|
input::{Button, ButtonController, Tri},
|
||||||
interrupt::VBlank,
|
interrupt::VBlank,
|
||||||
rng,
|
rng,
|
||||||
|
@ -159,12 +159,12 @@ struct Entity<'a> {
|
||||||
sprite: Object<'a>,
|
sprite: Object<'a>,
|
||||||
position: Vector2D<Number>,
|
position: Vector2D<Number>,
|
||||||
velocity: Vector2D<Number>,
|
velocity: Vector2D<Number>,
|
||||||
collision_mask: Rect<u16>,
|
collision_mask: Rect<Number>,
|
||||||
visible: bool,
|
visible: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Entity<'a> {
|
impl<'a> Entity<'a> {
|
||||||
fn new(object_controller: &'a ObjectController, collision_mask: Rect<u16>) -> Self {
|
fn new(object_controller: &'a ObjectController, collision_mask: Rect<Number>) -> Self {
|
||||||
let s = object_controller.sprite(LONG_SWORD_IDLE.sprite(0));
|
let s = object_controller.sprite(LONG_SWORD_IDLE.sprite(0));
|
||||||
let mut sprite = object_controller.object(s);
|
let mut sprite = object_controller.object(s);
|
||||||
sprite.set_priority(Priority::P1);
|
sprite.set_priority(Priority::P1);
|
||||||
|
@ -213,18 +213,7 @@ impl<'a> Entity<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collider(&self) -> Rect<Number> {
|
fn collider(&self) -> Rect<Number> {
|
||||||
let mut number_collision: Rect<Number> = Rect::new(
|
let mut number_collision = self.collision_mask;
|
||||||
(
|
|
||||||
self.collision_mask.position.x as i32,
|
|
||||||
self.collision_mask.position.y as i32,
|
|
||||||
)
|
|
||||||
.into(),
|
|
||||||
(
|
|
||||||
self.collision_mask.size.x as i32,
|
|
||||||
self.collision_mask.size.y as i32,
|
|
||||||
)
|
|
||||||
.into(),
|
|
||||||
);
|
|
||||||
number_collision.position =
|
number_collision.position =
|
||||||
self.position + number_collision.position - number_collision.size / 2;
|
self.position + number_collision.position - number_collision.size / 2;
|
||||||
number_collision
|
number_collision
|
||||||
|
@ -273,11 +262,12 @@ impl<'a> Entity<'a> {
|
||||||
(final_distance, has_collided)
|
(final_distance, has_collided)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn commit_with_fudge(&mut self, offset: Vector2D<Number>, fudge: Vector2D<i32>) {
|
fn commit_with_fudge(&mut self, offset: Vector2D<Number>, fudge: Vector2D<Number>) {
|
||||||
if !self.visible {
|
if !self.visible {
|
||||||
self.sprite.hide();
|
self.sprite.hide();
|
||||||
} else {
|
} else {
|
||||||
let position = (self.position - offset).floor() + fudge;
|
let position =
|
||||||
|
(self.position - offset + fudge + Vector2D::new(num!(0.5), num!(0.5))).floor();
|
||||||
self.sprite.set_position(position - (8, 8).into());
|
self.sprite.set_position(position - (8, 8).into());
|
||||||
if position.x < -8
|
if position.x < -8
|
||||||
|| position.x > WIDTH + 8
|
|| position.x > WIDTH + 8
|
||||||
|
@ -527,17 +517,14 @@ struct Player<'a> {
|
||||||
attack_timer: AttackTimer,
|
attack_timer: AttackTimer,
|
||||||
damage_cooldown: u16,
|
damage_cooldown: u16,
|
||||||
sword: SwordState,
|
sword: SwordState,
|
||||||
fudge_factor: Vector2D<i32>,
|
fudge_factor: Vector2D<Number>,
|
||||||
hurtbox: Option<Rect<Number>>,
|
hurtbox: Option<Rect<Number>>,
|
||||||
controllable: bool,
|
controllable: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Player<'a> {
|
impl<'a> Player<'a> {
|
||||||
fn new(object_controller: &'a ObjectController<'a>) -> Player {
|
fn new(object_controller: &'a ObjectController<'a>) -> Player {
|
||||||
let mut entity = Entity::new(
|
let mut entity = Entity::new(object_controller, Rect::new((0, 1).into(), (5, 10).into()));
|
||||||
object_controller,
|
|
||||||
Rect::new((0_u16, 0_u16).into(), (4_u16, 12_u16).into()),
|
|
||||||
);
|
|
||||||
let s = object_controller.sprite(LONG_SWORD_IDLE.sprite(0));
|
let s = object_controller.sprite(LONG_SWORD_IDLE.sprite(0));
|
||||||
entity.sprite.set_sprite(s);
|
entity.sprite.set_sprite(s);
|
||||||
entity.sprite.show();
|
entity.sprite.show();
|
||||||
|
@ -613,7 +600,7 @@ impl<'a> Player<'a> {
|
||||||
AttackTimer::Attack(a) => {
|
AttackTimer::Attack(a) => {
|
||||||
*a -= 1;
|
*a -= 1;
|
||||||
let frame = self.sword.attack_frame(*a);
|
let frame = self.sword.attack_frame(*a);
|
||||||
self.fudge_factor.x = self.sword.fudge(frame) * self.facing as i32;
|
self.fudge_factor.x = (self.sword.fudge(frame) * self.facing as i32).into();
|
||||||
let tag = self.sword.attack_tag();
|
let tag = self.sword.attack_tag();
|
||||||
let sprite = controller.sprite(tag.animation_sprite(frame as usize));
|
let sprite = controller.sprite(tag.animation_sprite(frame as usize));
|
||||||
self.entity.sprite.set_sprite(sprite);
|
self.entity.sprite.set_sprite(sprite);
|
||||||
|
@ -627,7 +614,7 @@ impl<'a> Player<'a> {
|
||||||
AttackTimer::Cooldown(a) => {
|
AttackTimer::Cooldown(a) => {
|
||||||
*a -= 1;
|
*a -= 1;
|
||||||
let frame = self.sword.hold_frame();
|
let frame = self.sword.hold_frame();
|
||||||
self.fudge_factor.x = self.sword.fudge(frame) * self.facing as i32;
|
self.fudge_factor.x = (self.sword.fudge(frame) * self.facing as i32).into();
|
||||||
let tag = self.sword.attack_tag();
|
let tag = self.sword.attack_tag();
|
||||||
let sprite = controller.sprite(tag.animation_sprite(frame as usize));
|
let sprite = controller.sprite(tag.animation_sprite(frame as usize));
|
||||||
self.entity.sprite.set_sprite(sprite);
|
self.entity.sprite.set_sprite(sprite);
|
||||||
|
@ -695,6 +682,8 @@ impl<'a> Player<'a> {
|
||||||
let gravity = gravity / 16;
|
let gravity = gravity / 16;
|
||||||
self.entity.velocity.y += gravity;
|
self.entity.velocity.y += gravity;
|
||||||
|
|
||||||
|
self.fudge_factor.x -= num!(1.5) * (self.facing as i32);
|
||||||
|
|
||||||
let fudge_number = (self.fudge_factor.x, self.fudge_factor.y).into();
|
let fudge_number = (self.fudge_factor.x, self.fudge_factor.y).into();
|
||||||
|
|
||||||
// convert the hurtbox to a location in the game
|
// convert the hurtbox to a location in the game
|
||||||
|
@ -1351,12 +1340,12 @@ enum UpdateInstruction {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EnemyData {
|
impl EnemyData {
|
||||||
fn collision_mask(&self) -> Rect<u16> {
|
fn collision_mask(&self) -> Rect<Number> {
|
||||||
match self {
|
match self {
|
||||||
EnemyData::Slime(_) => Rect::new((0u16, 0u16).into(), (4u16, 11u16).into()),
|
EnemyData::Slime(_) => Rect::new((0.into(), num!(1.5)).into(), (4, 11).into()),
|
||||||
EnemyData::Bat(_) => Rect::new((0u16, 0u16).into(), (12u16, 4u16).into()),
|
EnemyData::Bat(_) => Rect::new((0, 0).into(), (12, 4).into()),
|
||||||
EnemyData::MiniFlame(_) => Rect::new((0u16, 0u16).into(), (12u16, 12u16).into()),
|
EnemyData::MiniFlame(_) => Rect::new((0, 0).into(), (12, 12).into()),
|
||||||
EnemyData::Emu(_) => Rect::new((0u16, 0u16).into(), (7u16, 11u16).into()),
|
EnemyData::Emu(_) => Rect::new((0, 0).into(), (7, 11).into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1533,10 +1522,7 @@ impl<'a> Particle<'a> {
|
||||||
particle_data: ParticleData,
|
particle_data: ParticleData,
|
||||||
position: Vector2D<Number>,
|
position: Vector2D<Number>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut entity = Entity::new(
|
let mut entity = Entity::new(object_controller, Rect::new((0, 0).into(), (0, 0).into()));
|
||||||
object_controller,
|
|
||||||
Rect::new((0u16, 0u16).into(), (0u16, 0u16).into()),
|
|
||||||
);
|
|
||||||
|
|
||||||
entity.position = position;
|
entity.position = position;
|
||||||
|
|
||||||
|
@ -1611,10 +1597,7 @@ struct FollowingBoss<'a> {
|
||||||
|
|
||||||
impl<'a> FollowingBoss<'a> {
|
impl<'a> FollowingBoss<'a> {
|
||||||
fn new(object_controller: &'a ObjectController, position: Vector2D<Number>) -> Self {
|
fn new(object_controller: &'a ObjectController, position: Vector2D<Number>) -> Self {
|
||||||
let mut entity = Entity::new(
|
let mut entity = Entity::new(object_controller, Rect::new((0, 0).into(), (0, 0).into()));
|
||||||
object_controller,
|
|
||||||
Rect::new((0_u16, 0_u16).into(), (0_u16, 0_u16).into()),
|
|
||||||
);
|
|
||||||
entity.position = position;
|
entity.position = position;
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
@ -1695,10 +1678,7 @@ enum BossInstruction {
|
||||||
|
|
||||||
impl<'a> Boss<'a> {
|
impl<'a> Boss<'a> {
|
||||||
fn new(object_controller: &'a ObjectController, screen_coords: Vector2D<Number>) -> Self {
|
fn new(object_controller: &'a ObjectController, screen_coords: Vector2D<Number>) -> Self {
|
||||||
let mut entity = Entity::new(
|
let mut entity = Entity::new(object_controller, Rect::new((0, 0).into(), (28, 28).into()));
|
||||||
object_controller,
|
|
||||||
Rect::new((0_u16, 0_u16).into(), (28_u16, 28_u16).into()),
|
|
||||||
);
|
|
||||||
entity.position = screen_coords + (144, 136).into();
|
entity.position = screen_coords + (144, 136).into();
|
||||||
Self {
|
Self {
|
||||||
entity,
|
entity,
|
||||||
|
@ -1986,6 +1966,8 @@ impl<'a> Game<'a> {
|
||||||
self.shake_time -= 1;
|
self.shake_time -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let this_frame_offset = this_frame_offset.floor().into();
|
||||||
|
|
||||||
self.input.update();
|
self.input.update();
|
||||||
if let UpdateInstruction::CreateParticle(data, position) =
|
if let UpdateInstruction::CreateParticle(data, position) =
|
||||||
self.player
|
self.player
|
||||||
|
@ -1996,7 +1978,7 @@ impl<'a> Game<'a> {
|
||||||
self.particles.insert(new_particle);
|
self.particles.insert(new_particle);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut remove = Vec::with_capacity(10);
|
let mut remove = Vec::new();
|
||||||
for (idx, enemy) in self.enemies.iter_mut() {
|
for (idx, enemy) in self.enemies.iter_mut() {
|
||||||
if enemy.entity.position.x < self.offset.x - 8 {
|
if enemy.entity.position.x < self.offset.x - 8 {
|
||||||
remove.push(idx);
|
remove.push(idx);
|
||||||
|
@ -2086,10 +2068,6 @@ impl<'a> Game<'a> {
|
||||||
.commit_with_fudge(this_frame_offset, (0, 0).into());
|
.commit_with_fudge(this_frame_offset, (0, 0).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
self.level.background.commit(vram);
|
|
||||||
self.level.foreground.commit(vram);
|
|
||||||
self.level.clouds.commit(vram);
|
|
||||||
|
|
||||||
for i in remove {
|
for i in remove {
|
||||||
self.particles.remove(i);
|
self.particles.remove(i);
|
||||||
}
|
}
|
||||||
|
@ -2215,15 +2193,12 @@ fn game_with_level(gba: &mut agb::Gba) {
|
||||||
|
|
||||||
let mut start_at_boss = false;
|
let mut start_at_boss = false;
|
||||||
|
|
||||||
loop {
|
|
||||||
let (background, mut vram) = gba.display.video.tiled0();
|
let (background, mut vram) = gba.display.video.tiled0();
|
||||||
|
|
||||||
vram.set_background_palettes(background::PALETTES);
|
vram.set_background_palettes(background::PALETTES);
|
||||||
|
|
||||||
let tileset = TileSet::new(background::background.tiles, TileFormat::FourBpp);
|
let tileset = TileSet::new(background::background.tiles, TileFormat::FourBpp);
|
||||||
|
|
||||||
let object = gba.display.object.get();
|
let object = gba.display.object.get();
|
||||||
|
|
||||||
|
loop {
|
||||||
let backdrop = InfiniteScrolledMap::new(
|
let backdrop = InfiniteScrolledMap::new(
|
||||||
background.background(
|
background.background(
|
||||||
Priority::P2,
|
Priority::P2,
|
||||||
|
@ -2291,8 +2266,11 @@ fn game_with_level(gba: &mut agb::Gba) {
|
||||||
);
|
);
|
||||||
|
|
||||||
start_at_boss = loop {
|
start_at_boss = loop {
|
||||||
sfx.frame();
|
|
||||||
vblank.wait_for_vblank();
|
vblank.wait_for_vblank();
|
||||||
|
sfx.frame();
|
||||||
|
game.level.background.commit(&mut vram);
|
||||||
|
game.level.foreground.commit(&mut vram);
|
||||||
|
game.level.clouds.commit(&mut vram);
|
||||||
object.commit();
|
object.commit();
|
||||||
match game.advance_frame(&object, &mut vram, &mut sfx) {
|
match game.advance_frame(&object, &mut vram, &mut sfx) {
|
||||||
GameStatus::Continue => {}
|
GameStatus::Continue => {}
|
||||||
|
|
Loading…
Reference in a new issue