Getting Started
Welcome to the KFEngine documentation!
hello.lua
local a = 1
local function hello()
print(HelloWorld)
end
hello()
print(a)
Subscribe Engine event
- BeginFrame: signals the beginning of the new frame. Input and Network react to this to check for operating system window messages and arrived network packets.
- Update: application-wide logic update event. By default each update-enabled Scene reacts to this and triggers the scene update (more on this below.)
- SceneUpdate: variable timestep scene update. This is a good place to implement any scene logic that does not need to happen at a fixed step.
- SceneSubsystemUpdate: update scene-wide subsystems. Currently only the PhysicsWorld component listens to this, which causes it to step the physics simulation and send the following two events for each simulation step:
- PhysicsPreStep: called before the simulation iteration. Happens at a fixed rate (the physics FPS.) If fixed timestep logic updates are needed, this is a good event to listen to.
- PhysicsPostStep: called after the simulation iteration. Happens at the same rate as PhysicsPreStep.
- SmoothingUpdate: update SmoothedTransform components in network client scenes.
- ScenePostUpdate: variable timestep scene post-update. ParticleEmitter and AnimationController update themselves as a response to this event.
- PostUpdate: application-wide logic post-update event. The UI subsystem updates its logic here.
- RenderUpdate: Renderer updates its viewports here to prepare for rendering, and the UI generates render commands necessary to render the user interface.
- PostRenderUpdate: by default nothing hooks to this. This can be used to implement logic that requires the rendering views to be up-to-date, for example to do accurate raycasts. Scenes may not be modified at this point; especially scene objects may not be deleted or crashes may occur.
- EndFrame: signals the end of the frame. Before this, rendering the frame and measuring the next frame's timestep will have occurred.
app.lua
--game logic
local mygame = {}
local function OnUpdate(eventType, eventData)
print("----OnUpdate----")
--mygame:OnUpdate(eventType, eventData)
end
local function OnSceneUpdate(eventType, eventData)
print("----OnSceneUpdate----")
--mygame:OnSceneUpdate(eventType, eventData)
end
local function OnPostUpdate(eventType, eventData)
print("----OnPostUpdate----")
--mygame:OnPostUpdate(eventType, eventData)
end
local function OnPostRenderUpdate(eventType, eventData)
print("----OnPostRenderUpdate----")
--mygame:OnPostRenderUpdate(eventType, eventData)
end
function Start()
SubscribeToEvent("Update", OnUpdate)
SubscribeToEvent("SceneUpdate", OnSceneUpdate)
SubscribeToEvent("PostUpdate", OnPostUpdate)
SubscribeToEvent("PostRenderUpdate", OnPostRenderUpdate)
end
Create Simple Scene
app.lua
function mygame:CreateScene()
local scene = Scene()
scene:CreateComponent(Octree.id)
--create pipeline
local pipeline = scene:CreateComponent(RenderPipeline.id)
pipeline:SetAttribute("Color Space", Variant(1))-- 0: GammaLDR, 1: LinearLDR 2: LinearHDR
-- pipeline:SetAttribute("Specular Quality", Variant(2)) -- 0: Disabled 1: Simple, 2: Antialiased
pipeline:SetAttribute("PCF Kernel Size", Variant(5))
-- pipeline:SetAttribute("Bloom", Variant(true))
pipeline:SetAttribute("Post Process Antialiasing", Variant(2)) -- 0: "None" 1: "FXAA2" 2: "FXAA3"
--create zone
local zone = scene:CreateComponent(Zone.id)
zone:SetEnabled(true)
zone.bounding_box = math3d.BoundingBox(-1000.0, 1000.0)
zone.background_brightness = 0.5
zone:SetZoneTextureAttr("Textures/DefaultSkybox.xml")
--create sky
local skyNode = scene:CreateChild("Sky")
skyNode.rotation = math3d.Quaternion(1.0, 0.0, 0.0, 0.0)
local skybox = skyNode:CreateComponent(Skybox.id)
skybox:SetModel(cache:GetResource("Model", "Models/Box.mdl"))
skybox:SetMaterial(cache:GetResource("Material","Materials/DefaultSkybox.xml"))
--create light
local lightNode = scene:CreateChild("DirectionalLight")
lightNode.direction = math3d.Vector3(0.6, -1.0, 0.8) -- The direction vector does not need to be normalized
local light = lightNode:CreateComponent(Light.id)
light.light_type = LIGHT_DIRECTIONAL
light.cast_shadows = true
light.shadow_bias = BiasParameters(DEFAULT_CONSTANTBIAS, DEFAULT_SLOPESCALEDBIAS)
light.shadow_cascade = CascadeParameters(5.0, 12.0, 30.0, 100.0, DEFAULT_SHADOWFADESTART)
--create ground
local planeNode = scene:CreateChild("Plane");
planeNode.scale = math3d.Vector3(100.0, 1.0, 100.0)
local planeObject = planeNode:CreateComponent(StaticModel.id)
planeObject:SetModel(cache:GetResource("Model", "Models/Plane.mdl"))
local mtl = cache:GetResource("Material", "Materials/GridTiled.xml")
mtl:SetShaderParameter("UOffset", Variant(math3d.Vector4(100.0, 0.0, 0.0, 0.0)))
mtl:SetShaderParameter("VOffset", Variant(math3d.Vector4(0.0, 100.0, 0.0, 0.0)))
planeObject:SetMaterial(mtl)
-- create camera
local cameraNode = scene:CreateChild("Camera")
cameraNode.position = math3d.Vector3(0.0, 10.0, -35.0)
cameraNode:LookAt(math3d.Vector3(0.0, 0.0, 0.0))
local camera = cameraNode:CreateComponent(Camera.id)
camera.near_clip = 0.5
camera.far_clip = 500.0
--
self.scene = scene
self.camera_node = cameraNode
self.camera = camera
end
Add UI
app.lua
function mygame:CreateFairyGUI()
if not self.fairygui_scene then
self.fairygui_scene = FairyGUI.FairyGUIScene()
end
local uiroot = self.fairygui_scene.groot
FairyGUI.RegisterFont("default", "Fonts/FZY3JW.TTF")
FairyGUI.SetDesignResolutionSize(1280, 720)
FairyGUI.UIPackage.AddPackage("UI/VirtualList")
local view = FairyGUI.UIPackage.CreateObject("VirtualList", "Main")
uiroot:AddChild(view)
self.ui_view = view
self.gamelist = {
{author = "Author1", name = "Game1"},
{author = "Author2", name = "Game2"},
}
local list = self.ui_view:GetChild("mailList")
list:SetVirtual()
list:SetItemRenderer(function (index, obj)
--obj:GetController("c1"):SetSelectedIndex((index % 3 == 0) and 1 or 0)
--obj:GetController("IsRead"):SetSelectedIndex((index % 2 == 0) and 1 or 0);
obj:GetChild("timeText"):SetText("Author: " .. self.gamelist[index + 1].author)
obj:SetText(self.gamelist[index + 1].name)
end)
-- init items
list:SetNumItems(#self.gamelist)
local numItems = #self.gamelist
for i = 1, numItems do
local obj = list:GetChildAt(i - 1)
obj:AddClickListener(function (context)
local sender = context:GetSender()
-- context:GetData()
-- context:GetDataAsString()
print("click :", sender:GetText())
end)
end
-- dynamic append item
view:GetChild("play"):AddClickListener(function (context)
local list = self.ui_view:GetChild("mailList")
local num = #self.gamelist + 1
self.gamelist[num] = {author = "Author"..num, name = "Game"..num}
list:SetNumItems(num)
local obj = list:GetChildAt(num - 1)
obj:AddClickListener(function (context)
local sender = context:GetSender()
-- context:GetData()
-- context:GetDataAsString()
print("click :", sender:GetText())
end)
end)
FairyGUI.ReplaceScene(self.fairygui_scene)
end
Add Effect
app.lua
function mygame:CreateEffect()
local emitter = self.scene:CreateChild("effect")
emitter.position = math3d.Vector3(0.0, 2.0, 0.0)
local effect = emitter:CreateComponent(EffekseerEmitter.id)
effect:SetEffect("Effekseer/01_Suzuki01/001_magma_effect/aura.efk")
effect:SetLooping(true)
effect:Play()
self.emitter = emitter
self.effect = effect
end
Add Sound
app.lua
function mygame:CreateSound()
local bankname = "Sounds/Master.bank"
local ret = Audio.LoadBank(bankname)
if not ret then
print("LoadBank Faied. :", bankname)
end
local bankname = "Sounds/Master.strings.bank"
ret = Audio.LoadBank(bankname)
if not ret then
print("LoadBank Faied. :", bankname)
end
self.sound_attack = Audio.GetEvent("event:/Scene/attack")
end
Input handle
Full code
app.lua
local function GetGameDir(userid)
local platform = GetPlatformName()
if platform == "Android" or platform == "Web" or platform == "iOS" then
return filesystem:GetAppPreferencesDir("KFEngine", "KFPlayer") .. "Games/"..userid
elseif platform == "Windows" then
return filesystem:GetProgramDir() .. "Assets/Games/"..userid
end
end
--game logic
local mygame = {
yaw = 0,
pitch = 0,
MOVE_SPEED = 5.0
}
function mygame:Load(viewport, fairygui_scene)
self.game_dir = GetGameDir(10002)
virtual_filesystem:MountDir(self.game_dir)
local pkg = self.game_dir.."/data.pak"
if filesystem:FileExists(pkg) then
virtual_filesystem:MountPackage(pkg)
end
self.fairygui_scene = fairygui_scene
-- create level
self:CreateScene()
self:CreateFairyGUI()
self:CreateEffect()
self:CreateSound()
-- setup viewport
if not viewport then
self.viewport = Viewport(self.scene, self.camera)
else
viewport:SetScene(self.scene)
viewport:SetCamera(self.camera)
self.viewport = viewport
end
Effekseer.SetCamera(self.camera)
renderer_system:SetViewport(0, self.viewport)
if touchEnabled then
input_system.CreateJoystick(math3d.IntVector2(512, 512), 1.0)
end
end
function mygame:UnLoad()
-- cleanup resource
virtual_filesystem:Unmount(self.game_dir)
local pkg = self.game_dir.."/data.pak"
if filesystem:FileExists(pkg) then
virtual_filesystem:Unmount(pkg)
end
end
function mygame:CreateScene()
local scene = Scene()
scene:CreateComponent(Octree.id)
--create pipeline
local pipeline = scene:CreateComponent(RenderPipeline.id)
pipeline:SetAttribute("Color Space", Variant(1))-- 0: GammaLDR, 1: LinearLDR 2: LinearHDR
-- pipeline:SetAttribute("Specular Quality", Variant(2)) -- 0: Disabled 1: Simple, 2: Antialiased
pipeline:SetAttribute("PCF Kernel Size", Variant(5))
-- pipeline:SetAttribute("Bloom", Variant(true))
pipeline:SetAttribute("Post Process Antialiasing", Variant(2)) -- 0: "None" 1: "FXAA2" 2: "FXAA3"
--create zone
local zone = scene:CreateComponent(Zone.id)
zone:SetEnabled(true)
zone.bounding_box = math3d.BoundingBox(-1000.0, 1000.0)
zone.background_brightness = 0.5
zone:SetZoneTextureAttr("Textures/DefaultSkybox.xml")
--create sky
local skyNode = scene:CreateChild("Sky")
skyNode.rotation = math3d.Quaternion(1.0, 0.0, 0.0, 0.0)
local skybox = skyNode:CreateComponent(Skybox.id)
skybox:SetModel(cache:GetResource("Model", "Models/Box.mdl"))
skybox:SetMaterial(cache:GetResource("Material","Materials/DefaultSkybox.xml"))
--create light
local lightNode = scene:CreateChild("DirectionalLight")
lightNode.direction = math3d.Vector3(0.6, -1.0, 0.8) -- The direction vector does not need to be normalized
local light = lightNode:CreateComponent(Light.id)
light.light_type = LIGHT_DIRECTIONAL
light.cast_shadows = true
light.shadow_bias = BiasParameters(DEFAULT_CONSTANTBIAS, DEFAULT_SLOPESCALEDBIAS)
light.shadow_cascade = CascadeParameters(5.0, 12.0, 30.0, 100.0, DEFAULT_SHADOWFADESTART)
--create ground
local planeNode = scene:CreateChild("Plane");
planeNode.scale = math3d.Vector3(100.0, 1.0, 100.0)
local planeObject = planeNode:CreateComponent(StaticModel.id)
planeObject:SetModel(cache:GetResource("Model", "Models/Plane.mdl"))
local mtl = cache:GetResource("Material", "Materials/GridTiled.xml")
mtl:SetShaderParameter("UOffset", Variant(math3d.Vector4(100.0, 0.0, 0.0, 0.0)))
mtl:SetShaderParameter("VOffset", Variant(math3d.Vector4(0.0, 100.0, 0.0, 0.0)))
planeObject:SetMaterial(mtl)
-- create camera
local cameraNode = scene:CreateChild("Camera")
cameraNode.position = math3d.Vector3(0.0, 10.0, -35.0)
cameraNode:LookAt(math3d.Vector3(0.0, 0.0, 0.0))
local camera = cameraNode:CreateComponent(Camera.id)
camera.near_clip = 0.5
camera.far_clip = 500.0
--
self.scene = scene
self.camera_node = cameraNode
self.camera = camera
self.yaw = cameraNode.rotation:YawAngle()
self.pitch = cameraNode.rotation:PitchAngle()
end
function mygame:CreateFairyGUI()
if not self.fairygui_scene then
self.fairygui_scene = FairyGUI.FairyGUIScene()
end
local uiroot = self.fairygui_scene.groot
FairyGUI.RegisterFont("default", "Fonts/FZY3JW.TTF")
FairyGUI.SetDesignResolutionSize(1280, 720)
FairyGUI.UIPackage.AddPackage("UI/VirtualList")
local view = FairyGUI.UIPackage.CreateObject("VirtualList", "Main")
uiroot:AddChild(view)
self.ui_view = view
self.gamelist = {
{author = "Author1", name = "Game1"},
{author = "Author2", name = "Game2"},
}
local list = self.ui_view:GetChild("mailList")
list:SetVirtual()
list:SetItemRenderer(function (index, obj)
--obj:GetController("c1"):SetSelectedIndex((index % 3 == 0) and 1 or 0)
--obj:GetController("IsRead"):SetSelectedIndex((index % 2 == 0) and 1 or 0);
obj:GetChild("timeText"):SetText("Author: " .. self.gamelist[index + 1].author)
obj:SetText(self.gamelist[index + 1].name)
end)
-- init items
list:SetNumItems(#self.gamelist)
local numItems = #self.gamelist
for i = 1, numItems do
local obj = list:GetChildAt(i - 1)
obj:AddClickListener(function (context)
local sender = context:GetSender()
-- context:GetData()
-- context:GetDataAsString()
print("click :", sender:GetText())
end)
end
-- dynamic append item
view:GetChild("play"):AddClickListener(function (context)
local list = self.ui_view:GetChild("mailList")
local num = #self.gamelist + 1
self.gamelist[num] = {author = "Author"..num, name = "Game"..num}
list:SetNumItems(num)
local obj = list:GetChildAt(num - 1)
obj:AddClickListener(function (context)
local sender = context:GetSender()
-- context:GetData()
-- context:GetDataAsString()
print("click :", sender:GetText())
end)
end)
FairyGUI.ReplaceScene(self.fairygui_scene)
end
function mygame:CreateEffect()
local emitter = self.scene:CreateChild("effect")
emitter.position = math3d.Vector3(0.0, 2.0, 0.0)
local effect = emitter:CreateComponent(EffekseerEmitter.id)
effect:SetEffect("Effekseer/01_Suzuki01/001_magma_effect/aura.efk")
effect:SetLooping(true)
effect:Play()
self.emitter = emitter
self.effect = effect
end
function mygame:CreateSound()
local bankname = "Sounds/Master.bank"
local ret = Audio.LoadBank(bankname)
if not ret then
print("LoadBank Faied. :", bankname)
end
local bankname = "Sounds/Master.strings.bank"
ret = Audio.LoadBank(bankname)
if not ret then
print("LoadBank Faied. :", bankname)
end
self.sound_attack = Audio.GetEvent("event:/Scene/attack")
end
local sound_play_time = 0
function mygame:OnUpdate(eventType, eventData)
-- print("----mygame OnUpdate----")
local timeStep = eventData[ParamType.P_TIMESTEP]:GetFloat()
sound_play_time = sound_play_time + timeStep
if sound_play_time > 3.0 then
sound_play_time = 0
self.sound_attack:Start()
end
end
function mygame:OnSceneUpdate(eventType, eventData)
-- print("----mygame OnSceneUpdate----")
local timeStep = eventData[ParamType.P_TIMESTEP]:GetFloat()
if not FairyGUI.IsFocusUI() then
local rotate = false
if touchEnabled then
local TOUCH_SENSITIVITY = 2
for i = 0, input_system:GetNumTouches() - 1 do
if input_system.GetJoystickTouchID() ~= i then
local state = input_system:GetTouch(i)
if state.delta.x or state.delta.y then
local camera = self.camera_node:GetComponent(Camera.id)
if not camera then
return
end
self.yaw = self.yaw + TOUCH_SENSITIVITY * camera.fov / graphics_system.height * state.delta.x
self.pitch = math3d.ClampF(self.pitch + TOUCH_SENSITIVITY * camera.fov / graphics_system.height * state.delta.y, -90.0, 90.0)
rotate = true
end
break
end
end
elseif input_system:GetMouseButtonDown(input.MOUSEB_RIGHT) then
-- Mouse sensitivity as degrees per pixel
local MOUSE_SENSITIVITY = 0.1
-- Use this frame's mouse motion to adjust camera node yaw and pitch. Clamp the pitch between -90 and 90 degrees
local mouseMove = input_system.mouseMove
self.yaw = self.yaw + MOUSE_SENSITIVITY * mouseMove.x
self.pitch = math3d.ClampF(self.pitch + MOUSE_SENSITIVITY * mouseMove.y, -90.0, 90.0)
rotate = true
end
if rotate then
-- Construct new orientation for the camera scene node from yaw and pitch. Roll is fixed to zero
self.camera_node.rotation = math3d.Quaternion(self.pitch, self.yaw, 0.0)
end
end
local move = false
local controlDirection = math3d.Vector3(0.0, 0.0, 0.0)
if input_system.IsJoystickCapture() then
controlDirection = math3d.Quaternion(0.0, input_system.GetJoystickDegree() - 90, 0.0) * math3d.Vector3.BACK
controlDirection:Normalize()
move = true
elseif not FairyGUI.IsInputing() then
if input_system:GetKeyDown(input.KEY_W) then
controlDirection = math3d.Vector3.FORWARD
move = true
end
if input_system:GetKeyDown(input.KEY_S) then
controlDirection = math3d.Vector3.BACK
move = true
end
if input_system:GetKeyDown(input.KEY_A) then
controlDirection = math3d.Vector3.LEFT
move = true
end
if input_system:GetKeyDown(input.KEY_D) then
controlDirection = math3d.Vector3.RIGHT
move = true
end
end
if move then
self.camera_node:Translate(controlDirection * self.MOVE_SPEED * timeStep)
end
end
local function OnUpdate(eventType, eventData)
--print("----OnUpdate----")
mygame:OnUpdate(eventType, eventData)
end
local function OnSceneUpdate(eventType, eventData)
--print("----OnSceneUpdate----")
--mygame:OnSceneUpdate(eventType, eventData)
end
local function OnPostUpdate(eventType, eventData)
--print("----OnPostUpdate----")
--mygame:OnPostUpdate(eventType, eventData)
end
local function OnPostRenderUpdate(eventType, eventData)
--print("----OnPostRenderUpdate----")
--mygame:OnPostRenderUpdate(eventType, eventData)
end
function Start()
mygame:Load()
SubscribeToEvent("Update", OnUpdate)
SubscribeToEvent("SceneUpdate", OnSceneUpdate)
SubscribeToEvent("PostUpdate", OnPostUpdate)
SubscribeToEvent("PostRenderUpdate", OnPostRenderUpdate)
end