Change how ticking works
This commit is contained in:
+5
-4
@@ -14,10 +14,11 @@ export class Camera {
|
|||||||
near = 0.1;
|
near = 0.1;
|
||||||
far = 1000;
|
far = 1000;
|
||||||
|
|
||||||
setup(entity: Entity) {
|
setup(entity: Entity, partial_tick: number) {
|
||||||
this.x = entity.x;
|
const { x, y, z } = entity.render_position(partial_tick);
|
||||||
this.y = entity.y + entity.eye_height;
|
this.x = x;
|
||||||
this.z = entity.z;
|
this.y = y + entity.eye_height;
|
||||||
|
this.z = z;
|
||||||
this.yaw = entity.yaw;
|
this.yaw = entity.yaw;
|
||||||
this.pitch = entity.pitch;
|
this.pitch = entity.pitch;
|
||||||
}
|
}
|
||||||
|
|||||||
+46
-14
@@ -15,9 +15,13 @@ import { InputManager } from "./input_manager.ts";
|
|||||||
import { Connection } from "./network.ts";
|
import { Connection } from "./network.ts";
|
||||||
import { canvas, resize_canvas } from "./renderer/mod.ts";
|
import { canvas, resize_canvas } from "./renderer/mod.ts";
|
||||||
import { show_fatal_error } from "./fatal.ts";
|
import { show_fatal_error } from "./fatal.ts";
|
||||||
|
import { TICK_DELTA } from "$/common/constants.ts";
|
||||||
|
|
||||||
// the game client, like minecraft's Minecraft class: owns the level, the player, the screens and the renderer,
|
// after a long stall (a hidden tab, a debugger) the game skips ahead instead of running every missed tick at once
|
||||||
// and runs one step of the game every frame
|
const MAX_TICKS_PER_FRAME = 10;
|
||||||
|
|
||||||
|
// the game client, like minecraft's Minecraft class: owns the level, the player, the screens and the renderer.
|
||||||
|
// the game runs in fixed ticks (TICKS_PER_SECOND), input and drawing happen every frame
|
||||||
export class Client {
|
export class Client {
|
||||||
connection: Connection;
|
connection: Connection;
|
||||||
options = new Options();
|
options = new Options();
|
||||||
@@ -35,6 +39,12 @@ export class Client {
|
|||||||
hit_result: BlockHitResult | undefined;
|
hit_result: BlockHitResult | undefined;
|
||||||
debugging = false;
|
debugging = false;
|
||||||
|
|
||||||
|
// seconds of game time not ticked yet
|
||||||
|
#pending_time = 0;
|
||||||
|
// whether the attack button is held on a block, breaking it advances every tick
|
||||||
|
#attacking = false;
|
||||||
|
#stopped = false;
|
||||||
|
|
||||||
constructor(connection: Connection) {
|
constructor(connection: Connection) {
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
|
|
||||||
@@ -74,24 +84,46 @@ export class Client {
|
|||||||
this.screens.pop()?.on_close();
|
this.screens.pop()?.on_close();
|
||||||
}
|
}
|
||||||
|
|
||||||
// one frame: network, input, the level and its entities, then drawing
|
// one frame: input, as many ticks as are due, then drawing between the last tick and the next
|
||||||
run_frame(delta: number) {
|
run_frame(delta: number) {
|
||||||
|
this.screen?.on_tick(delta);
|
||||||
|
this.#handle_keybinds();
|
||||||
|
|
||||||
|
this.#pending_time += delta;
|
||||||
|
let ticks = 0;
|
||||||
|
while (this.#pending_time >= TICK_DELTA && ticks < MAX_TICKS_PER_FRAME) {
|
||||||
|
this.tick();
|
||||||
|
if (this.#stopped) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.#pending_time -= TICK_DELTA;
|
||||||
|
ticks += 1;
|
||||||
|
}
|
||||||
|
if (ticks === MAX_TICKS_PER_FRAME) {
|
||||||
|
this.#pending_time = Math.min(this.#pending_time, TICK_DELTA);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.game_renderer.render(this, this.#pending_time / TICK_DELTA);
|
||||||
|
}
|
||||||
|
|
||||||
|
// one step of the game: what the server sent, breaking, chunk loading, then every entity
|
||||||
|
tick() {
|
||||||
this.packet_listener.handle_packets();
|
this.packet_listener.handle_packets();
|
||||||
if (this.connection.closed) {
|
if (this.connection.closed) {
|
||||||
show_fatal_error("Lost connection to the server");
|
show_fatal_error("Lost connection to the server");
|
||||||
|
this.#stopped = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.screen?.on_tick(delta);
|
if (this.#attacking && this.hit_result) {
|
||||||
this.#handle_keybinds(delta);
|
this.game_mode.continue_destroy_block(this.hit_result);
|
||||||
|
}
|
||||||
|
|
||||||
this.level.update_loaded_chunks(this.player.x, this.player.z, this.options.render_distance);
|
this.level.update_loaded_chunks(this.player.x, this.player.z, this.options.render_distance);
|
||||||
this.level.tick(delta);
|
this.level.tick();
|
||||||
|
|
||||||
this.game_renderer.render(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#handle_keybinds(delta: number) {
|
#handle_keybinds() {
|
||||||
const options = this.options;
|
const options = this.options;
|
||||||
const player = this.player;
|
const player = this.player;
|
||||||
|
|
||||||
@@ -122,16 +154,18 @@ export class Client {
|
|||||||
InputManager.set_mouse_grabbed(!this.screen);
|
InputManager.set_mouse_grabbed(!this.screen);
|
||||||
|
|
||||||
this.hit_result = this.level.pick(player.x, player.y + player.eye_height, player.z, player.yaw, player.pitch);
|
this.hit_result = this.level.pick(player.x, player.y + player.eye_height, player.z, player.yaw, player.pitch);
|
||||||
this.#handle_block_interaction(delta);
|
this.#handle_block_interaction();
|
||||||
|
|
||||||
if (!this.screen) {
|
if (!this.screen) {
|
||||||
this.#handle_hotbar();
|
this.#handle_hotbar();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#handle_block_interaction(delta: number) {
|
// clicks happen right away, holding to break advances in tick()
|
||||||
|
#handle_block_interaction() {
|
||||||
const hit = this.hit_result;
|
const hit = this.hit_result;
|
||||||
const attacking = !this.screen && InputManager.is_mouse_down(0);
|
const attacking = !this.screen && InputManager.is_mouse_down(0);
|
||||||
|
this.#attacking = attacking;
|
||||||
|
|
||||||
if (!attacking || !hit) {
|
if (!attacking || !hit) {
|
||||||
this.game_mode.stop_destroy_block();
|
this.game_mode.stop_destroy_block();
|
||||||
@@ -144,9 +178,7 @@ export class Client {
|
|||||||
if (InputManager.is_mouse_pressed(0)) {
|
if (InputManager.is_mouse_pressed(0)) {
|
||||||
this.game_mode.start_destroy_block(hit);
|
this.game_mode.start_destroy_block(hit);
|
||||||
}
|
}
|
||||||
if (attacking) {
|
if (!attacking && InputManager.is_mouse_pressed(2)) {
|
||||||
this.game_mode.continue_destroy_block(hit, delta);
|
|
||||||
} else if (InputManager.is_mouse_pressed(2)) {
|
|
||||||
this.game_mode.use_item_on(hit);
|
this.game_mode.use_item_on(hit);
|
||||||
}
|
}
|
||||||
} else if (InputManager.is_mouse_pressed(2)) {
|
} else if (InputManager.is_mouse_pressed(2)) {
|
||||||
|
|||||||
+89
-48
@@ -1,6 +1,13 @@
|
|||||||
|
import { TICK_DELTA } from "$/common/constants.ts";
|
||||||
import type { ClientLevel } from "../level/client_level.ts";
|
import type { ClientLevel } from "../level/client_level.ts";
|
||||||
|
|
||||||
// anything that exists in the level and moves, like minecraft's Entity. position is the middle of its feet
|
// how far inside a block face still counts as touching it, so boxes resting exactly on a face don't snag
|
||||||
|
const EPSILON = 1e-7;
|
||||||
|
// the two axes that aren't the one being moved along
|
||||||
|
const OTHER_AXES = [[1, 2], [0, 2], [0, 1]] as const;
|
||||||
|
|
||||||
|
// anything that exists in the level and moves, like minecraft's Entity. position is the middle of its feet.
|
||||||
|
// it's simulated in fixed ticks (common/constants.ts), frames draw it between its last two positions
|
||||||
export abstract class Entity {
|
export abstract class Entity {
|
||||||
id: string;
|
id: string;
|
||||||
level: ClientLevel;
|
level: ClientLevel;
|
||||||
@@ -8,6 +15,10 @@ export abstract class Entity {
|
|||||||
x = 0;
|
x = 0;
|
||||||
y = 0;
|
y = 0;
|
||||||
z = 0;
|
z = 0;
|
||||||
|
// where it was at the start of the tick, what frames interpolate from
|
||||||
|
prev_x = 0;
|
||||||
|
prev_y = 0;
|
||||||
|
prev_z = 0;
|
||||||
// blocks per second
|
// blocks per second
|
||||||
vx = 0;
|
vx = 0;
|
||||||
vy = 0;
|
vy = 0;
|
||||||
@@ -39,62 +50,92 @@ export abstract class Entity {
|
|||||||
return this.colliding_y === 1;
|
return this.colliding_y === 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// jumps there, without interpolating from where it was
|
||||||
set_position(x: number, y: number, z: number) {
|
set_position(x: number, y: number, z: number) {
|
||||||
this.x = x;
|
this.x = this.prev_x = x;
|
||||||
this.y = y;
|
this.y = this.prev_y = y;
|
||||||
this.z = z;
|
this.z = this.prev_z = z;
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract tick(delta: number): void;
|
save_previous_position() {
|
||||||
|
this.prev_x = this.x;
|
||||||
// falls and moves by its velocity, stopping on each axis it would run into a block on
|
this.prev_y = this.y;
|
||||||
move(delta: number) {
|
this.prev_z = this.z;
|
||||||
this.vy += this.gravity * delta;
|
|
||||||
|
|
||||||
this.colliding_x = this.vx !== 0 && this.#collides(this.x + this.vx * delta, this.y, this.z)
|
|
||||||
? -Math.sign(this.vx)
|
|
||||||
: 0;
|
|
||||||
if (this.colliding_x !== 0) {
|
|
||||||
this.vx = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.colliding_y = this.vy !== 0 && this.#collides(this.x, this.y + this.vy * delta, this.z)
|
|
||||||
? -Math.sign(this.vy)
|
|
||||||
: 0;
|
|
||||||
if (this.colliding_y !== 0) {
|
|
||||||
this.vy = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.colliding_z = this.vz !== 0 && this.#collides(this.x, this.y, this.z + this.vz * delta)
|
|
||||||
? -Math.sign(this.vz)
|
|
||||||
: 0;
|
|
||||||
if (this.colliding_z !== 0) {
|
|
||||||
this.vz = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.x += this.vx * delta;
|
|
||||||
this.y += this.vy * delta;
|
|
||||||
this.z += this.vz * delta;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// whether the collision box at x/y/z overlaps any block, unloaded chunks included
|
// where to draw it, partial_tick is how far the frame is between the last tick and the next
|
||||||
#collides(x: number, y: number, z: number) {
|
render_position(partial_tick: number) {
|
||||||
const min_x = Math.floor(x - this.width / 2);
|
return {
|
||||||
const max_x = Math.floor(x + this.width / 2);
|
x: this.prev_x + (this.x - this.prev_x) * partial_tick,
|
||||||
const min_y = Math.floor(y);
|
y: this.prev_y + (this.y - this.prev_y) * partial_tick,
|
||||||
const max_y = Math.floor(y + this.height);
|
z: this.prev_z + (this.z - this.prev_z) * partial_tick,
|
||||||
const min_z = Math.floor(z - this.width / 2);
|
};
|
||||||
const max_z = Math.floor(z + this.width / 2);
|
}
|
||||||
|
|
||||||
for (let bx = min_x; bx <= max_x; bx++) {
|
// one step of the game, TICK_DELTA seconds
|
||||||
for (let by = min_y; by <= max_y; by++) {
|
abstract tick(): void;
|
||||||
for (let bz = min_z; bz <= max_z; bz++) {
|
|
||||||
if (this.level.get_block(bx, by, bz)) {
|
// falls and moves by its velocity for one tick. like minecraft it moves along y, then x, then z, each time only
|
||||||
return true;
|
// as far as it can before touching a block, and stops its velocity on the axes it hit something
|
||||||
|
move() {
|
||||||
|
// the average of this tick's start and end speed, so the arc is the same at any tick rate
|
||||||
|
const wanted_y = (this.vy + this.gravity * TICK_DELTA / 2) * TICK_DELTA;
|
||||||
|
this.vy += this.gravity * TICK_DELTA;
|
||||||
|
const wanted = [this.vx * TICK_DELTA, wanted_y, this.vz * TICK_DELTA];
|
||||||
|
|
||||||
|
const half = this.width / 2;
|
||||||
|
const min = [this.x - half, this.y, this.z - half];
|
||||||
|
const max = [this.x + half, this.y + this.height, this.z + half];
|
||||||
|
const moved = [0, 0, 0];
|
||||||
|
for (const axis of [1, 0, 2]) {
|
||||||
|
const distance = this.#clip(min, max, axis, wanted[axis]);
|
||||||
|
min[axis] += distance;
|
||||||
|
max[axis] += distance;
|
||||||
|
moved[axis] = distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hit = (axis: number) => moved[axis] !== wanted[axis] ? -Math.sign(wanted[axis]) : 0;
|
||||||
|
this.colliding_x = hit(0);
|
||||||
|
this.colliding_y = hit(1);
|
||||||
|
this.colliding_z = hit(2);
|
||||||
|
if (this.colliding_x !== 0) this.vx = 0;
|
||||||
|
if (this.colliding_y !== 0) this.vy = 0;
|
||||||
|
if (this.colliding_z !== 0) this.vz = 0;
|
||||||
|
|
||||||
|
this.x += moved[0];
|
||||||
|
this.y += moved[1];
|
||||||
|
this.z += moved[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
// how far the box can go along an axis before it runs into a block, unloaded chunks included.
|
||||||
|
// blocks it's already inside don't stop it, so it can get out of them
|
||||||
|
#clip(min: number[], max: number[], axis: number, distance: number) {
|
||||||
|
if (distance === 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [a, b] = OTHER_AXES[axis];
|
||||||
|
const from = Math.floor(Math.min(min[axis], min[axis] + distance));
|
||||||
|
const to = Math.floor(Math.max(max[axis], max[axis] + distance));
|
||||||
|
const position = [0, 0, 0];
|
||||||
|
|
||||||
|
for (let i = from; i <= to; i++) {
|
||||||
|
for (let j = Math.floor(min[a] + EPSILON); j <= Math.floor(max[a] - EPSILON); j++) {
|
||||||
|
for (let k = Math.floor(min[b] + EPSILON); k <= Math.floor(max[b] - EPSILON); k++) {
|
||||||
|
position[axis] = i;
|
||||||
|
position[a] = j;
|
||||||
|
position[b] = k;
|
||||||
|
if (!this.level.get_block(position[0], position[1], position[2])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (distance > 0 && i >= max[axis] - EPSILON) {
|
||||||
|
distance = Math.min(distance, i - max[axis]);
|
||||||
|
} else if (distance < 0 && i + 1 <= min[axis] + EPSILON) {
|
||||||
|
distance = Math.max(distance, i + 1 - min[axis]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return distance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import { InputManager } from "../input_manager.ts";
|
|||||||
import { ClientInventories } from "../inventory.ts";
|
import { ClientInventories } from "../inventory.ts";
|
||||||
import { Player } from "./player.ts";
|
import { Player } from "./player.ts";
|
||||||
|
|
||||||
// how often the position goes to the server
|
// the position goes to the server every other tick
|
||||||
const SEND_POSITION_INTERVAL = 1 / 10;
|
const SEND_POSITION_TICKS = 2;
|
||||||
|
|
||||||
// the player this client controls, like minecraft's LocalPlayer
|
// the player this client controls, like minecraft's LocalPlayer
|
||||||
export class LocalPlayer extends Player {
|
export class LocalPlayer extends Player {
|
||||||
@@ -14,22 +14,22 @@ export class LocalPlayer extends Player {
|
|||||||
move_speed = 4;
|
move_speed = 4;
|
||||||
jump_force = 6.7;
|
jump_force = 6.7;
|
||||||
|
|
||||||
#send_timer = 0;
|
#ticks_since_sent = 0;
|
||||||
|
|
||||||
constructor(client: Client, id: string, name: string) {
|
constructor(client: Client, id: string, name: string) {
|
||||||
super(client.level, id, name);
|
super(client.level, id, name);
|
||||||
this.client = client;
|
this.client = client;
|
||||||
}
|
}
|
||||||
|
|
||||||
tick(delta: number) {
|
tick() {
|
||||||
if (!this.client.screen) {
|
if (!this.client.screen) {
|
||||||
this.#apply_input();
|
this.#apply_input();
|
||||||
}
|
}
|
||||||
this.move(delta);
|
this.move();
|
||||||
this.#send_position(delta);
|
this.#send_position();
|
||||||
}
|
}
|
||||||
|
|
||||||
// turns with the mouse, like minecraft's MouseHandler.turnPlayer
|
// turns with the mouse every frame, not every tick, like minecraft's MouseHandler.turnPlayer
|
||||||
turn(mouse_dx: number, mouse_dy: number) {
|
turn(mouse_dx: number, mouse_dy: number) {
|
||||||
this.yaw += -mouse_dx * 0.001;
|
this.yaw += -mouse_dx * 0.001;
|
||||||
this.pitch += -mouse_dy * 0.001;
|
this.pitch += -mouse_dy * 0.001;
|
||||||
@@ -75,12 +75,12 @@ export class LocalPlayer extends Player {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#send_position(delta: number) {
|
#send_position() {
|
||||||
this.#send_timer += delta;
|
this.#ticks_since_sent += 1;
|
||||||
if (this.#send_timer < SEND_POSITION_INTERVAL) {
|
if (this.#ticks_since_sent < SEND_POSITION_TICKS) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.#send_timer = 0;
|
this.#ticks_since_sent = 0;
|
||||||
this.client.connection.send({
|
this.client.connection.send({
|
||||||
type: "move",
|
type: "move",
|
||||||
x: this.x,
|
x: this.x,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { PlayerInfo } from "$/common/protocol.ts";
|
import type { PlayerInfo } from "$/common/protocol.ts";
|
||||||
|
import { TICK_DELTA } from "$/common/constants.ts";
|
||||||
import type { ClientLevel } from "../level/client_level.ts";
|
import type { ClientLevel } from "../level/client_level.ts";
|
||||||
import { Player } from "./player.ts";
|
import { Player } from "./player.ts";
|
||||||
|
|
||||||
@@ -32,8 +33,8 @@ export class RemotePlayer extends Player {
|
|||||||
this.pitch = pitch;
|
this.pitch = pitch;
|
||||||
}
|
}
|
||||||
|
|
||||||
tick(delta: number) {
|
tick() {
|
||||||
const t = Math.min(1, delta * SMOOTHING);
|
const t = Math.min(1, TICK_DELTA * SMOOTHING);
|
||||||
this.x += (this.target_x - this.x) * t;
|
this.x += (this.target_x - this.x) * t;
|
||||||
this.y += (this.target_y - this.y) * t;
|
this.y += (this.target_y - this.y) * t;
|
||||||
this.z += (this.target_z - this.z) * t;
|
this.z += (this.target_z - this.z) * t;
|
||||||
|
|||||||
+4
-4
@@ -1,5 +1,5 @@
|
|||||||
import { BlockRegistry, EverythingRegistry, ItemRegistry } from "$/common/everything_registry.ts";
|
import { BlockRegistry, EverythingRegistry, ItemRegistry } from "$/common/everything_registry.ts";
|
||||||
import { AIR, FACE_OFFSETS } from "$/common/constants.ts";
|
import { AIR, FACE_OFFSETS, TICK_DELTA } from "$/common/constants.ts";
|
||||||
import type { Client } from "./client.ts";
|
import type { Client } from "./client.ts";
|
||||||
import type { BlockHitResult } from "./level/client_level.ts";
|
import type { BlockHitResult } from "./level/client_level.ts";
|
||||||
|
|
||||||
@@ -22,14 +22,14 @@ export class MultiPlayerGameMode {
|
|||||||
this.client.connection.send({ type: "hit_block", x: hit.x, y: hit.y, z: hit.z });
|
this.client.connection.send({ type: "hit_block", x: hit.x, y: hit.y, z: hit.z });
|
||||||
}
|
}
|
||||||
|
|
||||||
// called every frame the attack button is held on a block
|
// called every tick the attack button is held on a block
|
||||||
continue_destroy_block(hit: BlockHitResult, delta: number) {
|
continue_destroy_block(hit: BlockHitResult) {
|
||||||
const block_info = EverythingRegistry.get_by_id<BlockRegistry>("blocks", hit.block)!;
|
const block_info = EverythingRegistry.get_by_id<BlockRegistry>("blocks", hit.block)!;
|
||||||
const tool_type = this.#held_item()?.tool_type;
|
const tool_type = this.#held_item()?.tool_type;
|
||||||
|
|
||||||
this.destroy_pos = { x: hit.x, y: hit.y, z: hit.z };
|
this.destroy_pos = { x: hit.x, y: hit.y, z: hit.z };
|
||||||
this.destroy_time = block_info.toughness ?? 9999;
|
this.destroy_time = block_info.toughness ?? 9999;
|
||||||
this.destroy_progress += delta * (tool_type === block_info.tool_to_break ? 2 : 1);
|
this.destroy_progress += TICK_DELTA * (tool_type === block_info.tool_to_break ? 2 : 1);
|
||||||
|
|
||||||
if (this.destroy_progress >= this.destroy_time) {
|
if (this.destroy_progress >= this.destroy_time) {
|
||||||
// show it right away, the server decides drops and corrects us if it disagrees
|
// show it right away, the server decides drops and corrects us if it disagrees
|
||||||
|
|||||||
@@ -123,9 +123,10 @@ export class ClientLevel {
|
|||||||
this.entities.delete(id);
|
this.entities.delete(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
tick(delta: number) {
|
tick() {
|
||||||
for (const entity of this.entities.values()) {
|
for (const entity of this.entities.values()) {
|
||||||
entity.tick(delta);
|
entity.save_previous_position();
|
||||||
|
entity.tick();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,14 +10,15 @@ export class GameRenderer {
|
|||||||
hud = new Hud();
|
hud = new Hud();
|
||||||
debug_overlay = new DebugOverlay();
|
debug_overlay = new DebugOverlay();
|
||||||
|
|
||||||
render(client: Client) {
|
// partial_tick is how far this frame is between the last tick and the next, entities are drawn in between
|
||||||
|
render(client: Client, partial_tick: number) {
|
||||||
const camera = client.camera;
|
const camera = client.camera;
|
||||||
camera.setup(client.player);
|
camera.setup(client.player, partial_tick);
|
||||||
|
|
||||||
begin_mode_3d(camera);
|
begin_mode_3d(camera);
|
||||||
this.level_renderer.render_opaque(client.level, camera);
|
this.level_renderer.render_opaque(client.level, camera);
|
||||||
this.level_renderer.render_destroy_progress(client.game_mode);
|
this.level_renderer.render_destroy_progress(client.game_mode);
|
||||||
this.level_renderer.render_entities(client.level, client.player);
|
this.level_renderer.render_entities(client.level, client.player, partial_tick);
|
||||||
this.level_renderer.render_translucent(client.level, camera);
|
this.level_renderer.render_translucent(client.level, camera);
|
||||||
end_mode_3d();
|
end_mode_3d();
|
||||||
|
|
||||||
|
|||||||
@@ -75,13 +75,13 @@ export class LevelRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// every entity but the one the camera is in
|
// every entity but the one the camera is in
|
||||||
render_entities(level: ClientLevel, camera_entity: Entity) {
|
render_entities(level: ClientLevel, camera_entity: Entity, partial_tick: number) {
|
||||||
flush_batch();
|
flush_batch();
|
||||||
set_current_texture(white_tex!);
|
set_current_texture(white_tex!);
|
||||||
|
|
||||||
for (const entity of level.entities.values()) {
|
for (const entity of level.entities.values()) {
|
||||||
if (entity !== camera_entity && entity instanceof RemotePlayer) {
|
if (entity !== camera_entity && entity instanceof RemotePlayer) {
|
||||||
render_player(entity);
|
render_player(entity, partial_tick);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,9 +119,9 @@ export class LevelRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// a box body and head in the player's color
|
// a box body and head in the player's color
|
||||||
function render_player(player: RemotePlayer) {
|
function render_player(player: RemotePlayer, partial_tick: number) {
|
||||||
const [r, g, b] = player.color;
|
const [r, g, b] = player.color;
|
||||||
const { x, y, z } = player;
|
const { x, y, z } = player.render_position(partial_tick);
|
||||||
|
|
||||||
// body
|
// body
|
||||||
push_box(x - 0.3, y, z - 0.15, 0.6, 1.3, 0.3, r, g, b);
|
push_box(x - 0.3, y, z - 0.15, 0.6, 1.3, 0.3, r, g, b);
|
||||||
|
|||||||
Reference in New Issue
Block a user