I'm not really familiar with this code, so bear with me.
If I understand correctly you use this line to draw the player character - or something akin to that - at the center of the screen, right?
blit(player, screen_width() / 2.0 - 32.0, screen_height() / 2.0 - 32.0, 32.0, 32.0,);
I converted the gif to mp4 so I could seek to the end, and found that it draws a lot of green squares with a something in the center, and screen_width() / 2.0
and screen_height() / 2.0
looks very center, such middle, to me. So I believe this line is responsible of that.
That tells me that the bounds of the screen are screen_width()
and screen_height()
. And apparently the size of the sprite is 32
by 32
, which I'll assume is also the size of the tiles. Furthermore, this line tells me that the blit
function is used to draw. So, let us see the blit
function:
fn blit(t: Texture2D, x: f32, y: f32, w: f32, h: f32) {
draw_texture_ex(
t,
x,
y,
WHITE,
DrawTextureParams {
source: Some(Rect::new(0.0, 0.0, w, h)),
..Default::default()
},
);
}
Ok, now I have a chance at parsing this:
blit(grass, i as f32 * 32.0 + worldx, *j as f32 + worldy, 32.0, 32.0);
So this renders:
- The
grass
texture.
- At x coordinate
i as f32 * 32.0 + worldx
- At y coordinate
*j as f32 + worldy
- Width
32
- Height
32
New approach
You are going to render grass
anyway, right? Let us start there.
Now, the size of the screen is screen_width()
and screen_height()
, which we can divide by the size of the tile and round upwards to figure out how many tiles must there be visible.
That got to be something like this:
let screen_w_in_tiles = ceilf(screen_width() / 32.0);
let screen_h_in_tiles = ceilf(screen_height() / 32.0);
I could be introducing an off-by-one error. Yet, I'm erring on the upside, so worst case scenario you render a line of tile out of bounds of the screen.
Then, let us figure out the x
and y
coordinates in tile space of the center of the screen. That got to be something like this:
let center_tile_x = ((screen_width() / 2.0) - worldx) / 32.0;
let center_tile_y = ((screen_height() / 2.0) - worldy) / 32.0;
Thus, we get a range in tile space. Something like this:
let lo_tile_x = floorf(center_tile_x - screen_w_in_tiles / 2.0) as i32;
let hi_tile_x = ceilf(center_tile_x + screen_w_in_tiles / 2.0) as i32;
let lo_tile_y = floorf(center_tile_y - screen_h_in_tiles / 2.0) as i32;
let hi_tile_y = ceilf(center_tile_y + screen_h_in_tiles / 2.0) as i32;
Then you can loop:
for i in lo_tile_x..hi_tile_x {
for j in lo_tile_y..hi_tile_y {
blit(
grass,
i as f32 * 32.0 + worldx,
j as f32 * 32.0 + worldy,
32.0,
32.0
);
}
}
I appreciate the symmetry between the x
and y
, and the lack of *j
Furthermore, presumably you can query your data structure with an arbitrary x
and y
coordinates. I suppose you could make a class for your data structure, or at least make a function that borrows it, and takes the x
and y
coordinates and tells you the kind of tile you need to render, and then you can select a texture based on that.
So you could something like this:
for i in lo_tile_x..hi_tile_x {
for j in lo_tile_y..hi_tile_y {
let texture = choose_texture(
get_tile_type(
i,
j,
/*other arguments that reference to the vector or whatever*/
)
);
blit(
texture,
i as f32 * 32.0 + worldx,
j as f32 * 32.0 + worldy,
32.0,
32.0
);
}
}
I also didn't put check for the bounds, you can add that too.
Old approach
Now, are those coordinates within screen_width()
and screen_height()
? Well, let me try to understand the coordinates first.
i
: you take the first index.
i as f32
: you cast i
to a 32 bit float, right?
i as f32 * 32.0
: scale it by 32, which is the width.
i as f32 * 32.0 + worldx
: and add some offset.
And…
*j
: what? What is this? Some pointer? Yeah, I don't know.
- ???
Ok, calm, breathe. I'll work with what I know, and hopefully that is sufficient for you to take it all the way there.
What is the first value of i
that would be on the screen? It would be the smallest value such that i as f32 * 32.0 + worldx >= 0
. So, set up the equation, and solve for i
:
i as f32 * 32.0 + worldx = 0
=>
i as f32 * 32.0 + worldx - worldx = 0 - worldx
=>
i as f32 * 32.0 = -worldx
=>
i as f32 * 32.0 / 32.0 = -worldx / 32.0
=>
i as f32 = -worldx / 32.0
Thus, you can start iterating at -worldx / 32.0
(round downwards and cast to integer).
Now, where do we stop? What is the last value of i
that would be on the screen? That would be the greater value such that i as f32 * 32.0 + worldx <= screen_width()
. Spoiler: more equations!
i as f32 * 32.0 + worldx = screen_width()
=>
i as f32 * 32.0 + worldx - worldx = screen_width() - worldx
=>
i as f32 * 32.0 = screen_width() - worldx
=>
i as f32 * 32.0 / 32.0 = (screen_width() - worldx) / 32.0
=>
i as f32 = (screen_width() - worldx) / 32.0
So want to loop up to (screen_width() - worldx) / 32.0
(round upwards and cast to integer).
Be aware that the range from -worldx / 32.0
(round downwards and cast to integer) to (screen_width() - worldx) / 32.0
(round upwards and cast to integer) might go outside the vector you have, so you may want to clamp the range.
I believe that instead of this:
for i in 1..tile_list.len() {
// …
}
It should be something similar to this:
let lo_i = min(1, floorf(-worldx / 32.0) as i32)
let hi_i = max(tile_list.len(), ceilf((screen_width() - worldx) / 32.0) as i32)
for i in lo_i..hi_i {
// …
}
By the way, I have finally settled in floorf
and ceilf
from libm
after disappointments with the functions rust has for floats.
Also, by the way, are you sure the vector goes from 1
to .len()
? That does not sound right to me, but hey this is arcane code to me. Which reminds me, the vertical bounds are up to you because I don't know what *j
is. However I suppose the formulas would look similar to these.