@hiberworld/code-kit-express
v3.6.1
Published
Composable Content Components for Code-Kit
Downloads
34
Keywords
Readme
Hiber Engine: Code-Kit Express
Code-Kit is the scripting interface for the Hiber Engine API, and Code-Kit _Express is an additional layer built on top of it.
Code-Kit Express includes a set of functions called helpers, which provides a simplified interface and hide the complexity of working directly with the underlying layer.
Please see Code-Kit for more information.
Hiber Engine is a engine specifically designed for the Metaverse. It is built using standard web technology, including C++ converted to Web Assembly, and adds features such as multiplayer, interconnections between virtual worlds, interoperability, and support for web3 technologies. As it is built using standard web technology, there is no need to download any additional software. Hiber Engine creates a layer for the Metaverse on top of the existing internet.
Hiber Engine is the engine used on the web site www.hiberworld.com.
Setup
Dependencies
installation
Run yarn
to install dependencies
Local development
Run yarn dev
to start a local development server
Keyboard shortcuts
V - Toggle fly mode
L - Open list of assets and materials
M - Open game menu
IDE
What code editor to use is up to you but we recommend VS Code from MicroSoft, it's free and widely used.
CodeKit Express is developed in TypeScript also from MicroSoft. Most properties are typed and have auto completion. In most IDE´s you access it with Ctrl+Space
.
Getting started
To start working with CodeKit Express create a new project in the project folder src/projects. The easiest way to do this is to duplicate the project empty.ts. Make sure to change name from empty in the file to the name of your project, for example testProject
.
You should now have a project file that looks like this:
import { addTo, draw, getGround, getRoot } from '../helpers';
export const testProject = () => {
// Create the root object
const root = getRoot();
// Add something to stand on
const ground = getGround({ hilly: 0.1 });
addTo(root, ground);
// Draw the scene
return draw(root);
};
To run this project you must add it to src/index.ts like this.
import { CodeKit } from '@hiberworld/code-kit';
import { addKeyboardShortcuts } from './helpers';
import { onHotReload } from './helpers/onHotReload';
import { testProject } from './projects/testProject';
if (import.meta.hot) {
onHotReload();
import.meta.hot.accept();
}
const scene = testProject(); // <-- Add project here
const hs = new CodeKit();
addKeyboardShortcuts(hs);
hs.renderScene('canvas', scene);
The scene should look like this:
Naming
CodeKit Express consists of a set of functions called helpers. Most of these helpers create and return something or adds something to an existing object.
get-helpers
Helpers that create and returns something starts with get-, like getPrefab. An object returned from a get-helper will only be visible in the scene if it's added to another parent object. This is done using the helper addTo.
const cube = getPrefab({ p: [0, 0, 5] });
addTo(root, cube);
add-helpers
Helpers that adds something starts with add-, like addPointLight. An add-helper needs a target object to work as it should. This target helper is also returned.
const lightSource = getGroup({ p: [0, 2, 5] });
addPointLight(lightSource);
addTo(root, lightSource);
Adding a 3D object to the scene
3D objects in HiberEngine are called Prefabs. A prefab can contain a number of 3D objects, animations. particles etc. The most simple way to add a prefab to the scene is by using the helper getPrefab:
const cube = getPrefab();
addTo(root, cube);
or shorter
addTo(root, getPrefab());
Add this to your project file and a cube will appear on the scene.
import { addTo, draw, getGround, getPrefab, getRoot } from '../helpers';
export const testProject = () => {
// Create the root object
const root = getRoot();
// Add something to stand on
const ground = getGround({ hilly: 0.1 });
addTo(root, ground);
const cube = getPrefab(); // <-- your code here
addTo(root, cube);
// Draw the scene
return draw(root);
};
This code places a cube at the location of the avatar in the center of the scene. However, you may want to customize the cube further. Let's explore how to modify the code to add additional properties to the cube.
const cube = getPrefab({
id: 'cube_01', // What prefab to use
p: [0, 0, 5], // Position, default is 0, 0, 0
r: [0, 0, 0], // Rotation in Euler/degrees, default is 0, 0, 0
s: [1, 1, 1], // Scale, default is 1, 1, 1
});
In the code above we define what prefab to use and sets its position, rotation and scale.
If we would like to add anything else but a cube we change the line:
id: 'cube_01',
To something else, like this:
id: 'wooden_wheel_01',
This will add a wooden wheel the the scene. Lets rotate and scale it to see how it works.
const wheel = getPrefab({
id: 'wooden_wheel_01',
p: [0, 0, 5],
r: [0, -30, 0],
s: [4, 4, 4],
});
Materials
Changing material on a prefab is easy. Just add the material
property. To see available materials hit Ctrl+Space after the colon in material:
.
const wheel = getPrefab({
id: 'wooden_wheel_01',
p: [0, 0, 5],
r: [0, -30, 0],
s: [4, 4, 4],
material: 't_rainbow_02',
});
Assets and materials
To see what assets and materials press L
to open the Assets browser. It will open a new tab in your browser.
- Click on the thumbnails to copy the assets id
- Double click to copy is as prefab code
- Click the Change to Material button to the right top to see available materials
Ground
Unless you want to create a space or water world a ground can be handy. There's a helper for that called getGround. You have seen it being used in the examples above.
In it's most basic form it looks like this:
addTo(root, getGround());
This will add a flat grass plane. You can define how hilly it should be with the property hilly
.
const ground = getGround({ hilly: 10 });
addTo(root, ground);
Material
To change the material of the ground, similar to how it is done with a prefab, you can use the material
property.
const ground = getGround({ hilly: 0, material: 't_stone_floor_01' });
Size
The size of the ground can be changes in two ways. The easiest way is to just change the scale.
const ground = getGround({ hilly: 1, s: pop3(3) });
Tips: Use pop3 to populate a vector3 with the same number.
Another way is to change the number of ground blocks being used by changing repeatX
and repeatZ
from it's default value 4. You can of course also use both repeatX
, repeatZ
and scale
.
const ground = getGround({ hilly: 1, repeatX: 1, repeatZ: 10 });
A cave can easily be created using two grounds.
addTo(root, getGround({ hilly: 1, material: 't_sand_01' }));
addTo(root, getGround({ hilly: 10, material: 't_rock_03', r: [180, 0, 0], s: [1, 4, 1] }));
Images
You add an image to an object like this:
const cube = getPrefab({ p: [0, 0, 6] });
addImage(cube, 'https://cdn.hibervr.com/demo/img/Mona.jpg');
addTo(root, cube);
Images for testing can be found here: src/assets/artData.ts
Poster
If you want to add an image like a flat surface, like a poster, you can use the helper getImage:
const img = getImage({ src: 'https://cdn.hibervr.com/demo/img/Mona.jpg', ratio: 671 / 1000 });
addTo(root, img);
Framed image
You can also add an image with a frame by using getFramedImage.
frame
property equals:
0 = Golden frame
1 = Stone frame
2 = Modern frame
-1 = random
const img = getFramedImage({
src: 'https://cdn.hibervr.com/demo/img/Mona.jpg',
ratio: 671 / 1000,
frame: 0,
p: [0, 1, 0],
s: pop3(1),
});
addTo(root, img);
Video
You add a video to an object using addVideo.
const cube = getPrefab({ p: [0, 0, 6] });
addVideo(cube, { src: 'https://cdn.hibervr.com/video/bike.mp4' });
addTo(root, cube);
Here are some videos to try out:
- bike
- brand
- dj
- Cubes_Waves
- Kaleidoscope
- Tunnel_Loop
- Cubes_Loop
VideoPlayer
You can also add a video using the helper getVideoPlayer. This adds an video to a flat surface.
addTo(root, getVideoPlayer({ src: 'https://cdn.hibervr.com/video/bike.mp4', p: [0, 0, 5] }));
Interactive Media Asset
Interactive Media Asset (IMA) could be described as links to other sites. Say you have an image of an product and would like to make it possible to open up a wab page with more information about the product. Then you should use an IMA.
Any object, not group or node, could be turned into a IMA. You add it using addInteractiveMediaAsset.
const img = getImage({ src: 'https://cdn.hibervr.com/demo/img/Mona.jpg', ratio: 671 / 1000 });
addInteractiveMediaAsset(img, {
header: 'Mona Lisa',
body: 'Also called Portrait of Lisa Hibertini',
url: 'https://hiberworld.com/',
});
addTo(root, img);
Portal
PORTAL NEEDS MORE LOVE TO WORK 100%
If you instead want to link to another world on www.HiberWorld.com you use portals. To add a portal you use the helper getPortal.
Find LevelID
in the url of a world.
Ex: https://hiber.hiberworld.com/?Launch.InitLevelID=1237022115291292
const prt = getPortal({ LevelID: 1237022115291292, p: [0, 0, 10] });
addTo(root, prt);
Clone
If you want another prefab with the same properties you can use the clone function, getClone.
const wheel2 = getClone(wheel, { p: [-2, 3, 8], r: [-22, 33, 44] });
This code will create a clone of the prefab. If you provide new values for the properties of the prefab as arguments, they will override the corresponding values in the cloned prefab.
Symmetrical clone
Say you want to build something symmetrical like a spaceship or a vehicle. Then the getMirrorX, getMirrorY and getMirrorZ is handy. These functions are a special set of the cone function. They clone an item and then flip the rotation and position to make it look like a mirrored object.
Say you hav wedge placed next to a scaled box, and you want to create a mirror of it on the other side.
const hull = getPrefab({ p: [0, 0, 5], s: [1, 1, 3] });
addTo(root, hull);
let wing = getPrefab({ p: [1, 1, 0], s: [1, 1, 1], id: 'wedge_45', r: [0, 0, -90] });
addTo(hull, wing);
It's done like this using getMirrorX:
const hull = getPrefab({ p: [0, 0, 5], s: [1, 1, 3] });
addTo(root, hull);
let wing = getPrefab({ p: [1, 1, 0], s: [1, 1, 1], id: 'wedge_45', r: [0, 0, -90] });
addTo(hull, [wing, getMirrorX(wing)]);
Note: It's not perfect since there's no distortion happening, just rotation and repositioning.
Groups
You can also group prefabs together. This is powerful if you want to rotate, scale, move or clone and entire group of objects.
Create a group using getGroup:
let grp = getGroup({p: [0, 0, 5]});
This will place a group 5 meters (1 meter = 3.28 feet) in front of you.
let wheelGrp = getGroup({ p: [0, 0, 5] });
addTo(root, wheelGrp);
const wheel = getPrefab({
name: 'wheel',
id: 'wooden_wheel_01',
p: [0, 0, 0], // <-- Change to 0, 0, 0 to place it in the center of the group
r: [0, -30, 0],
s: [4, 4, 4],
material: 't_rainbow_02',
});
addTo(wheelGrp, wheel);
The tentacle
Not just groups can contain other objects, in fact any object can have children. With a few lines of code you can get this:
let parent = addTo(root, getPrefab({ id: 'color_cube_02_04', p: [0, 0, 20], r: [0, 0, -20] }));
for (let i = 0; i < 20; i++) parent = addTo(parent, getClone(parent, { p: [0, 2.2, 0], r: [0, 15, 15], s: pop3(0.9) }));
Animations
CodeKit Express has a number of ways to animate/tween objects.
Tween using key frames
Tween an object using key frames using the helper getTween.
Note: p
, r
, and s
defined in the key frames is relative values to the objects position
, rotation
and scale
.
// Create key frames, ts = timeStamp 0 - 1
const kf: KeyFrameOptions[] = [
{ ts: 0.25, p: [0, 2, 0], easing: 'LINEAR' }, // <-- default easing is EASE_IN_OUT_QUAD
{ ts: 0.5, p: [2, 2, 0] },
{ ts: 0.5, r: [0, 90, 180] },
{ ts: 0.75, p: [2, 0, 0] },
{ ts: 0.8, r: [0, 90, 0] },
{ ts: 1, p: [0, 0, 0] },
];
// Create a wrapper for the tweens, use this to place, rotate and scale the animated object.
const tweenWrapper = getGroup({ p: [0, 1, 5] });
addTo(root, tweenWrapper);
// Create object to tween and apply the tween
const spr = getPrefab({ id: 'sphere_01' });
addTo(tweenWrapper, getTween(spr, kf, { loopBehaviour: 'RESTART' }));
Tween options
loopBehaviour
- RESTART | REVERSE, default: REVERSEduration
- The length of the tween, default: 2progress
- Where in the tween should it start. Ex 0.5 starts 50% in, default: 0speedMultiplier
- Change the speed, default: 1
Spin
If you want a more basic animation like making an object spin. Heres the basic code for that.
// First create an spinning group
const spinner = getSpinningGroup({ p: [0, 0, 10], axis: 'y', r: [10, 20, 30] });
addTo(root, spinner);
// Create what should be spinning and add it to the group.
const cube = getPrefab();
addTo(spinner, cube);
You can get the same result by using the helper getSpinning.
const cube = getPrefab();
addTo(root, getSpinning(cube, { p: [0, 0, 10], axis: 'y', r: [10, 20, 30] }));
Note: The cube is wrapped in an Animated Node. You must add the object returned from getSpinning to the scene to get the cube spinning.
Move
The get an object to move you can use key frames like this.
const kf: KeyFrameOptions[] = [
{ ts: 0, p: [0, 0, 0] },
{ ts: 1, p: [0, 4, 0] },
];
const tweenWrapper = getGroup({ p: [0, 0, 10], r: [0, 30, 20] });
addTo(root, tweenWrapper);
const spr = getPrefab();
addTo(tweenWrapper, getTween(spr, kf, { loopBehaviour: 'REVERSE' }));
You can get the same result by using the helper getMoving.
const cube = getPrefab();
addTo(root, getMoving(cube, { to: [0, 4, 0], p: [0, 0, 10], r: [0, 30, 20] }));
Note: to
is where it should move to relative to tho objects position p
.
Environment
Change environment by adding an environment id to the draw function.
To see available environment hit Ctrl+Space after return draw(root, '
.
return draw(root, 'starry_night_01');
Lights
HiberEngine has two kind of lights. A light source is added to an object or group.
Note: Lights are heavy to render, don't add to many and don't add many close to each other.
PointLight
A point light can be added using addPointLight.
const pl = addPointLight(getGroup({ p: [0, 0, 6] }));
addTo(root, pl);
Or use the helper getPointLight
addTo(root, getPointLight({ p: [0, 1, 5] }));
Spotlight
Spotlight can be added in a similar way using addSpotlight.
const sl = addSpotlight(getGroup({ p: [0, 2, 6] }));
addTo(root, sl);
Or use the helper getSpotlight
addTo(root, getSpotlight({ p: [0, 2, 6] }));
Tips: To get a random color use the helper getRandomColor
addTo(root, getSpotlight({ p: [0, 2, 6], color: getRandomColor() }));
Sound
You can add a sound source to an object using addAudio.
const cube = getPrefab({ p: [0, 0, 5] });
addAudio(cube, { id: 'a_m_iconic_bullet_01' });
addTo(root, cube);
id is the id of the sound file in the Hiber assets database. As for now you can only use sounds from there.
There are various properties that can be used to make the sound behave differently. To make things easier, there are three helpers available instead of trying out the properties individually.
Point Sound
Add a point sound to a object using addPointSound. A point sound is a helper for addAudio where radius is use to define the size of the audio source. The audio will get stronger the closer you get to the source. Good for example birds singing in a tree.
radius
in meters, approximately.
const cube = getPrefab({ p: [0, 0, 5] });
addPointSound(cube, 'a_am_birds_and_insects_01', 10);
addTo(root, cube);
Omnipresent Sound
Add a omnipresent sound source using addOmnipresentSound. This adds a point of sound with the same volume in the entire world.
addOmnipresentSound(root, 'a_am_rainforest_01');
Sound Cell
Add a sound cell using addSoundCell. The sound will play with the same volume within the radius. A sound cell is useful in situations where you want sound to be played within a certain area, such as a room, but not outside of it.
radius
in meters, approximately.
const soundCenter = getGroup({ p: [0, 1, 0] });
addSoundCell(soundCenter, 'a_mu_bass_meant_jazz_kevin_macleod_01', 10);
addTo(root, soundCenter);
Dummies
You can place dummy avatars in a scene using getDummy.
addTo(root, getDummy());
You change avatar and it's emot like this:
addTo(root, getDummy({ avatar: 'red_robot', emot: 'an_default_emote_shuffledance' }));
You can also change properties like scale
, speed
and material
.
addTo(
root,
getDummy({
avatar: 'red_robot',
emot: 'an_default_emote_shuffledance',
speed: 0.2,
s: pop3(3),
material: 't_lava_01',
})
);
Or why not add a video?
let dancer = getDummy({
avatar: 'red_robot',
emot: 'an_default_emote_shuffledance',
speed: 0.5,
s: pop3(7),
p: [0, -6, 0],
});
addVideo(dancer, { src: 'https://cdn.hibervr.com/video/Tunnel_Loop.mp4', emissiveStrength: 2 });
addTo(root, dancer);
Ready Player Me
You can also add customized avatars from Ready Player Me. For the URL to work it must be an HiberWorld avatar created on https://readyplayer.me/hub/avatars.
The URL is found in the three-dots-menu -> Copy .glb URL
.
Add a Ready player me dummy using getExternalAvatar.
const rpmAvatar = getExternalAvatar({
url: 'https://api.readyplayer.me/v1/avatars/6345138aabc8b2f2ee9c21b1.glb',
skeleton: 'SKG_RPM_MALE',
p: [0, 0, 5],
r: [0, 180, 0],
emot: 'an_default_backflip',
});
addTo(root, rpmAvatar);
Particles
HiberEngine has a highly capable particle system. To add particles to an object use addParticles
const ball = getPrefab({ id: 'sphere_01', p: [0, 1, 3], s: pop3(0.3) });
addParticles(ball);
addTo(root, ball);
You can use particles om animated objects. Important to remember to add the particle to the object to be animated and not to the Animated Node returned from getMoving
.
const ball = getPrefab({ id: 'sphere_01', p: [0, 1, 2], s: pop3(0.3) });
addParticles(ball); // <-- add particles here
addTo(root, getMoving(ball));
Add properties for the particle system like this:
const ball = getPrefab({ id: 'sphere_01', p: [0, 0, 2], s: pop3(0.3) });
addParticles(ball, {
particleTexture: 'particle_star_01_albedo',
sizeStart: 0.25,
sizeEnd: 0.1,
forceGravity: -10,
randomVelocity: [2, 5, 2],
gradientTexture: 'particle_gradient1_albedo',
colorEnd: [1.0, 1.0, 1.0, 1.0],
collisionMode: 'BOUNCE',
});
addTo(root, getMoving(ball));
All settings found here addParticles.ts.
You can also add particles using the particle presets found here PARTICLE_PRESETS.ts.
const cube = getPrefab({ p: [0, 0, 5] });
addParticles(cube, PARTICLE_PRESETS.starDust);
addTo(root, getMoving(cube));
Get a room
Create a room by using getRoom. Returns a room with walls defined in the wallTypes property. Walls are created using getSlice3 and getWall
const room = getRoom({
p: [0, 0, 5],
wallTypes: [WALL_TYPES.DOOR, WALL_TYPES.WINDOW, WALL_TYPES.DOOR, WALL_TYPES.PLAIN],
});
addTo(root, room);
You can also create a house complex using getComplex.
const floor = getComplex({
p: [0, 0.1, 10],
dim: 5,
height: 5,
rooms: [
{ coords: [0, 0, 0], wallTypes: ['PLAIN', 'PLAIN', 'DOOR', 'NONE'] }, // Define wall types
{ coords: [1, 0, 0], roomType: 'CONNECTED' }, // Or randomize them using roomType
{ coords: [1, 0, 1], roomType: 'OPEN' },
{ coords: [1, 0, 2], roomType: 'CONNECTED' },
{ coords: [2, 0, 2], roomType: 'WINDOWS' },
{ coords: [2, 0, 0], roomType: 'CONNECTED' },
],
});
addTo(root, floor);
Random vales
Since we want a scene to look the same every time we load it, we can't use the regular Math.random()
function. Instead we use a random seed function.
A random seed function returns a sequence of numbers that appears to be random, but will be the same every time it is run, based on a value called a seed that is provided to the function.
You get random seed values using getRandomSeed.
getRandomSeed().value();
Or use if the shorthand alias:
rsd().value();
This will return a value between 0 and 1. Since in this case no seed is sent to getRandomSeed(seen) the globalSeed value will be used found here globalSeed.
If you are not satisfied with the sequence of random numbers generated by the function, you can specify your own seed number by providing any integer value greater than or equal to 1.
getRandomSeed(42).value();
This feature can be useful if, for example, you have a helper that is placing trees randomly on the scene and another helper that is creating an art gallery with a random layout. Both helpers are using the global random seed by default. If you are not satisfied with the way the trees are being placed, one way to change this is to use a different seed value. You can also change the global seed value but it will effect all values using it.
You can get a random between two values using:
getRandomSeed().range(0, 100); // float
Math.round(getRandomSeed().range(0, 100)); // int
And random item from an array.
getRandomSeed().fromArray(myArray);
Scape
Scape is a helper that is used to distribute objects in a specific area, such as a landscape, cityscape etc. It is an efficient way to add a large number of objects to a scene in a seemingly random placement.
With a few lines of code using getScape
you can get create something like this.
addTo(root, getScape(SCAPE_PRESETS.foliagesBig));
addTo(root, getScape(SCAPE_PRESETS.rocks));
addTo(root, getScape(SCAPE_PRESETS.palmTree));
The properties for scape is this:
areaSize
The size of the scape area. Default is 200.
spaceSize
and spaceSizeDiff
The smallest and biggest space to place and item inside.
"spaceSize" is between spaceSize and spaceSize + spaceSizeDiff.
Default 10 and 20.
gapSize
and gapSizeDiff
Along with this spaces you can also insert gaps with no items inside them.
gapFreq
How frequent should the gaps be? For example if you set gapSize and spaceMax to the same number and gapFreq to zero you will ge ta grid.
boundaryMin
If you want a glade in the middle of your scape area you define it with boundaryMin. The number is between 0 - 1.5.
boundaryMax
Similar to boundaryMin but defined the outer limit of the forest sp to speak.
itemScale
The start scale of the items placed in the space.
itemScaleDiff
Items placed in spaces will have a scale between itemScale and itemScale + itemScaleDiff.
prefabId
Which prefabs to use. Can be a string or an array of prefabsIds.
material
Which material to use. Can be a string or an array of materialIds.
tiltiness
How mush should the items tilt. Good for creating a wild forest. Value between 0 and 1.
randomRotation
Will rotate the item randomly between 0 and 360 on the Y axis.
Over this you can also p
, r
, s
and seed
.
Lets have a closer look. Calling getScape with it's default values and you will get this:
addTo(root, getScape());
As mentioned above in the section about random seed, you can change the layout of the rocks by adding a custom seed value.
addTo(root, getScape({ seed: 42 }));
If we want to make the number of rocks denser we can change spaceSize
and spaceSizeDiff
values.
addTo(root, getScape({ spaceSize: 5, spaceSizeDiff: 5 }));
If we would like to get tilted candy assets in different sizes we could type this:
addTo(
root,
getScape({
spaceSize: 5,
spaceSizeDiff: 5,
prefabId: ['lollipop_03', 'lollipop_01', 'lollipop_02', 'marshmallow_pillar_01'],
tiltiness: 0.1,
itemScale: 2,
itemScaleDiff: 5,
boundaryMin: 0.05,
})
);
As seen above you can also use a preset. To alter a preset you can use this:
addTo(root, getScape({ ...SCAPE_PRESETS.foliages, ...{ r: [180, 0, 0], p: [0, 10, 0] } }));
onEach
Scape also has a property called onEach
which allows you to specify a function to be called for each space that is created, rather than automatically inserting a prefab. This allows you to have more control over what happens in each space.
Using that we can do something like this:
const lightCluster = getScape({
p: [0, 4, 0],
spaceSize: 10,
spaceSizeDiff: 20,
itemScaleDiff: 3,
onEach: (props: any) => {
const cyl = addSpotlight(getPrefab({ ...props, ...{ id: rsd().fromArray(['tron_e3_orange', 'tron_e3_green']) } }), {
color: getRandomColor(),
});
return cyl; // <-- Important to return what's been added.
},
});
addTo(root, getSpinning(lightCluster, { p: [0, 0, 10], axis: 'y', r: [6, 0, 0], duration: 16 }));
Note: It's important to return what's been added from the onEach
function
Documentation
Check the docs for mor in-depth information.