Last active
October 13, 2022 14:13
-
-
Save duydang2311/992d5cb5f9a6267cf39cc84e8636dbbb to your computer and use it in GitHub Desktop.
[alt:V] RmlUi document class for drawing 3D texts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as alt from 'alt-client'; | |
export interface WorldElementData { | |
pos: () => alt.Vector3; | |
text: () => string; | |
losCheck?: () => boolean; | |
rangeSquared?: number; | |
} | |
export interface WorldDocumentConfig { | |
rmlPath: string; | |
renderInterval: () => number; | |
} | |
export interface IWorldDocument { | |
add: (data: WorldElementData) => alt.RmlElement; | |
remove: (element: alt.RmlElement) => boolean; | |
} | |
export class WorldDocument implements IWorldDocument { | |
private readonly dataMap = new Map<alt.RmlElement, WorldElementData>(); | |
private readonly document: alt.RmlDocument; | |
private renderInterval: number = 0; | |
private now: number = 0; | |
constructor(config: WorldDocumentConfig) { | |
this.document = new alt.RmlDocument(config.rmlPath); | |
this.document.show(false, false); | |
this.document.body.style['font-family'] = '<your font>'; // to fix font not loaded issue with Rml although font-family is specified in rcss | |
this.renderInterval = config.renderInterval(); | |
alt.setInterval(() => { | |
this.renderInterval = config.renderInterval(); | |
}, 250); | |
alt.everyTick(this.render); | |
} | |
private readonly render = () => { | |
const now = Date.now(); | |
if (now - this.now < this.renderInterval) { | |
return; | |
} | |
this.now = now; | |
const local = alt.Player.local; | |
const localPos = local.pos; | |
for (const [element, data] of this.dataMap) { | |
const pos = data.pos(); | |
const dist = !data.rangeSquared | |
? 1 | |
: this.clampIfLower( | |
0, | |
Math.round((1 - this.distanceSquared(localPos, pos) / data.rangeSquared) * 1000) / 1000); | |
if ( | |
!alt.isPointOnScreen(pos.x, pos.y, pos.z) || | |
(data.losCheck && !data.losCheck()) | |
) { | |
element.style['opacity'] = '0'; | |
continue; | |
} | |
const screen = alt.worldToScreen(pos.x, pos.y, pos.z); | |
element.style['top'] = `${Math.round(screen.y * 100) / 100}px`; | |
element.style['left'] = `${Math.round(screen.x * 100) / 100}px`; | |
element.style['opacity'] = '' + dist; | |
element.style['font-size'] = `${dist}em'; // or `${0.5 + dist * 0.5}em` or for slighter scale | |
element.innerRML = data.text(); | |
} | |
}; | |
public readonly add = (data: WorldElementData): alt.RmlElement => { | |
const element = this.document.createElement('p'); | |
element.style['position'] = 'absolute'; | |
element.style['white-space'] = 'nowrap'; | |
element.innerRML = data.text(); | |
this.document.body.appendChild(element); | |
this.dataMap.set(element, data); | |
return element; | |
}; | |
public readonly remove = (element: alt.RmlElement) => { | |
this.document.body.removeChild(element); | |
return this.dataMap.delete(element); | |
}; | |
private readonly clampIfLower = (min: number, value: number) => { | |
return value < min ? min : value; | |
}; | |
private readonly distanceSquared = (v1: alt.Vector3, v2: alt.Vector3) => { | |
const dx = v1.x - v2.x; | |
const dy = v1.y - v2.y; | |
const dz = v1.z - v2.z; | |
return dx * dx + dy * dy + dz * dz; | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment