Libgdx 3d particle error on initialization - kotlin

I'm having a rendering issue for 3d particles created with the flame particle editor in libgdx.
I'm using a particle system to render 3d particles created with flame, I followed the tutorial in the libgdx' official site: code taken from: , but when I try to render them, I get the following error:
Cannot invoke "com.badlogic.gdx.graphics.g3d.particles.batches.ParticleBatch.draw(com.badlogic.gdx.graphics.g3d.particles.renderers.ParticleControllerRenderData)" because "this.batch" is null
Here's my code:
class MyGame : ApplicationAdapter(){
//3d stuff
lateinit var cam: PerspectiveCamera
lateinit var camController: CameraInputController
lateinit var modelBatch: ModelBatch
lateinit var environment: Environment
lateinit var model: Model
lateinit var mb:ModelBuilder
//lateinit var ground:ModelInstance
lateinit var ecs:World
//creating the bullet world
lateinit var broadphase: btBroadphaseInterface
lateinit var dynamicsWorld: btDynamicsWorld
lateinit var constraintSolver: btConstraintSolver
lateinit var collisionConfig: btCollisionConfiguration
lateinit var dispatcher: btDispatcher
lateinit var contactListener: MyContactListener
//debug drawer
lateinit var debugDrawer: DebugDrawer
//particle effects
lateinit var particleSystem:ParticleSystem
lateinit var pointSpriteBatch:PointSpriteParticleBatch
override fun create() {
Bullet.init()
//initializing 3d stuff
mb = ModelBuilder()
initializeWorld()
initializeBullet()
ecs= world {
injectables {
add(mb)
add(modelBatch)
add(cam)
add(camController)
add(environment)
add(dynamicsWorld)
add(debugDrawer)
add(contactListener)
}
components {
onAdd(BulletComponent, BulletComponent.onAdd)
onRemove(BulletComponent, BulletComponent.onRemove)
}
systems {
//the model system works just fine!
add(ModelSystems())
//this is going to be fun
add(BulletSystem())
}
}
with(ecs){//creating the ground
mb.begin()
//ok so for every model you create you giv it an id (makes everything more flexible)
mb.node().id="ground"
mb.part(
"ground",
GL20.GL_TRIANGLES,
(VertexAttributes.Usage.Position or VertexAttributes.Usage.Normal).toLong(),
Material(
ColorAttribute.createDiffuse(Color.RED)
)
)
.box(0f, 0f, 0f, 5f, 1f, 5f)
model=mb.end()
var motionState=MotionState(ModelInstance(model).transform)
val ground=entity {
//it+=ModelComponent(ModelInstance(model))
it+=BulletComponent(motionState, btBoxShape(Vector3(2.5f, 0.5f, 2.5f)),0f)
}
}
}
private fun initializeBullet() {
collisionConfig = btDefaultCollisionConfiguration()
dispatcher = btCollisionDispatcher(collisionConfig)
broadphase = btDbvtBroadphase()
//setting the dynamic world
constraintSolver= btSequentialImpulseConstraintSolver()
dynamicsWorld= btDiscreteDynamicsWorld(dispatcher,broadphase, constraintSolver, collisionConfig)
dynamicsWorld.gravity= Vector3(0f, -1.8f, 0f)
contactListener = MyContactListener()
debugDrawer=DebugDrawer().apply {
debugMode= btIDebugDraw.DebugDrawModes.DBG_DrawWireframe
}
dynamicsWorld.debugDrawer=debugDrawer
}
fun initializeWorld(){
modelBatch = ModelBatch()
environment = Environment()
environment.set(ColorAttribute(ColorAttribute.AmbientLight, 0.4f, 0.4f, 0.4f, 1f))
environment.add(DirectionalLight().set(0.8f, 0.8f, 0.8f, -1f, -0.8f, -0.2f))
cam = PerspectiveCamera(
67f, Gdx.graphics.width.toFloat(), Gdx.graphics.height
.toFloat()
)
cam.position[3f, 7f] = 10f
cam.lookAt(0f, 4f, 0f)
cam.near = 1f
cam.far = 300f
cam.update()
camController = CameraInputController(cam)
Gdx.input.inputProcessor = camController
//initializing particle effects
particleSystem=ParticleSystem()
pointSpriteBatch = PointSpriteParticleBatch()
pointSpriteBatch.setCamera(cam)
particleSystem.add(pointSpriteBatch)
//adding the particle effect to an asset manager
var assets=AssetManager()
var loadParam=ParticleEffectLoader.ParticleEffectLoadParameter(particleSystem.batches)
assets.load("myfirst3dparticles.pfx",
ParticleEffect::class.java, loadParam)
assets.finishLoading()
//passing the particle effect from the asset manager to the particle system
val originalEffect: ParticleEffect = assets.get("myfirst3dparticles.pfx")
// we cannot use the originalEffect, we must make a copy each time we create new particle effect
val effect = originalEffect.copy()
effect.init()
effect.start() // optional: particle will begin playing immediately
particleSystem.add(effect)
}
override fun render() {
Gdx.gl.glClearColor(0f, 0f, 0f, 1f)
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT or GL20.GL_DEPTH_BUFFER_BIT)
Gdx.gl.glEnable(GL20.GL_TEXTURE_2D)
//always update the camera if you're gonna make changes to it
//cam.update()
camController.update()
modelBatch.begin(cam)
//modelBatch.render(ground, environment)
//setting particles to be rendered
particleSystem.update()
particleSystem.begin()
particleSystem.draw()
particleSystem.end()
modelBatch.render(particleSystem)
ecs.update(Gdx.graphics.deltaTime)
modelBatch.end()
}

Related

Adaptive Banner for Android 11 (SDK Version 30) - Kotlin - Deprecated

Good afternoon. This is my first consultation in the community. I am trying to implement an adaptive banner using the official documentation, but the example has deprecated terms and the help that Android Studio provides is confusing for me.
I was looking for hours in the forum and I did not find an answer to help me.
I would appreciate if you could help me implement an adaptive banner with Kotiln for SDK 30.
private lateinit var adView: AdView
private val adSize: AdSize
get() {
val display = windowManager.defaultDisplay
val outMetrics = DisplayMetrics()
display.getMetrics(outMetrics)
val density = outMetrics.density
var adWidthPixels = frameAnuncio.width.toFloat()
if (adWidthPixels == 0f) {
adWidthPixels = outMetrics.widthPixels.toFloat()
}
val adWidth = (adWidthPixels / density).toInt()
return AdSize.getCurrentOrientationAnchoredAdaptiveBannerAdSize(this, adWidth)
}
Pic1
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_receta)
val toolbar: Toolbar = findViewById(R.id.toolbar)
setSupportActionBar(toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
MobileAds.initialize(this) { }
adView = AdView(this)
frameAnuncio.addView(adView)
loadBanner()
}
private fun loadBanner() {
adView.adUnitId = AD_UNIT_ID
adView.adSize = adSize
val adRequest = AdRequest.Builder().addTestDevice(AdRequest.DEVICE_ID_EMULATOR).build()
adView.loadAd(adRequest)
}
companion object {
private val AD_UNIT_ID = "ca-app-pub-3940256099942544~3347511713"
}
Pic2
The Admob dependency is:
Implementation 'com.google.android.gms: play-services-ads: 19.6.0'
Thank you
Try to use this it gives same value to the original code snippet from the documentation
private AdSize getAdSize() {
DisplayMetrics outMetrics = getResources().getDisplayMetrics();
float widthPixels = getScreenWidth();
float density = outMetrics.density;
int adWidth = (int) (widthPixels / density);
return AdSize.getCurrentOrientationAnchoredAdaptiveBannerAdSize(this, adWidth);
}
public int getScreenWidth() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
WindowMetrics windowMetrics = getWindowManager().getCurrentWindowMetrics();
Insets insets = windowMetrics.getWindowInsets()
.getInsetsIgnoringVisibility(WindowInsets.Type.systemBars());
return windowMetrics.getBounds().width() - insets.left - insets.right;
} else {
DisplayMetrics outMetrics = getResources().getDisplayMetrics();
return outMetrics.widthPixels;
}
}
For the first part, you can use windowManager.currentDisplayMetrics to get access to window bounds. And resources.displayMetrics for access to the density.
private val adSize: AdSize
get() {
val metrics = windowManager.currentWindowMetrics
val density = resources.displayMetrics.density
var adWidthPixels = frameAnuncio.width.toFloat()
if (adWidthPixels == 0f) {
adWidthPixels = metrics.bounds.width().toFloat()
}
val adWidth = (adWidthPixels / density).toInt()
return AdSize.getCurrentOrientationAnchoredAdaptiveBannerAdSize(this, adWidth)
}
The new way of adding a test device is explained in the documentation here.

tornadofx listview is creating one additional null listcellfragment than items in the list

I have a ViewModel for a ListView with 3 players in it:
object PlayerListViewModel : ViewModel() {
lateinit var players : ObservableList<Player>
init{
}
fun loadPlayers(){
players = Engine.selectedGame.players.asObservable()
}
}
class PlayerListView : View() {
private val vm = PlayerListViewModel
override val root = VBox()
init {
vm.loadPlayers()
root.replaceChildren {
style {
spacing = 25.px
alignment = Pos.CENTER
padding = box(0.px, 15.px)
}
listview(vm.players){
style{
background = Background.EMPTY
prefWidth = 300.px
}
isFocusTraversable = false
isMouseTransparent = true
cellFragment(PlayerCardFragment::class)
}
}
}
}
For some reason the listview is creating 4 PlayerCardFragments, with the first having a null item property and the last 3 having the correct Player item reference. This is the PlayerCardFragment definition:
class PlayerCardFragment : ListCellFragment<Player>() {
private val logger = KotlinLogging.logger { }
private val vm = PlayerViewModel().bindTo(this)
private lateinit var nameLabel : Label
private lateinit var scoreLabel : Label
override val root = hbox {
addClass(UIAppStyle.playerCard)
nameLabel = label(vm.name) { addClass(UIAppStyle.nameLabel) }
scoreLabel = label(vm.score) { addClass(UIAppStyle.scoreLabel) }
}
init {
logger.debug { "Initializing fragment for ${this.item} and ${vm.name.value}" }
EventBus.channel(EngineEvent.PlayerChanged::class)
.observeOnFx()
.subscribe() {
vm.rollback() //force viewmodel (PlayerViewModel) refresh since model (Player) does not implement observable properties
logger.debug { "${vm.name.value}'s turn is ${vm.myTurn.value}" }
root.toggleClass(UIAppStyle.selected, vm.myTurn)
}
}
When running the application, the PlayerCardFragment initializations print out "Initializing fragment for null and null" four times, but the list appears perfectly correctly with the 3 Player items. Later during execution, wnen there is an Engine.PlayerChanged event received, the Oberver function prints:
"null's turn is false"
"Adam's turn is false"
"Chad's turn is true"
"Kyle's turn is false"
These are the correct players, with the correct turn statuses. The listview appears perfectly well with the styling changes. I'm just not sure where that first null ListCellFragment is coming from.
It seems like you're trying to give ItemViewModel functionality to a view model by having everything around it change. Why not change the PlayerViewModel to have the functionality instead? Easiest way to imagine is to create bindings out of generic properties, then have them all changed and committed with by listening to the itemProperty:
class Player(var foo: String?, var bar: Int?)
class PlayerViewModel() : ItemViewModel<Player>() {
val foo = bind { SimpleStringProperty() }
val bar = bind { SimpleIntegerProperty() }
init {
itemProperty.onChange {
foo.value = it?.foo
bar.value = it?.bar
}
}
override fun onCommit() {
item?.let { player ->
player.foo = foo.value
player.bar = bar.value?.toInt()
}
}
}
Is it pretty? No. Does it keep you from having to implement an event system? Yes.

Kotlin Coroutines Run blocking not working as expected and Cannot block execution of for loops

In my app i'm executing two for loops however those for loops need to scheduled in a order here is the use case:
There are two for loops :
1- ImageStickerslist
2-TextStickerslist
What i want to do is after imagestickerslist if properly finised only then textstickerslist will be executed.
Here imagesticker list consists of url path which is used to load images from glide however if those images are of high resolution it eventually makes the thread continue even if the image is not yet loaded from url. To solve this tried adding blocking calls to glide on ready and full method but it won't prove to be of any help. I'm very confused how blocking calls work any help into this will be really appreciated.
Here's my code where for loops are executed:
runBlocking {
launch {
imagestickers.forEach {
runBlocking {
var image = it.path
var x = it.x
var y = it.y
image!!.log()
setimagestickers(image!!, x!!, y!!, it.width!!, it.height!!)
}
}
}.join()
textstickers.forEach {
runBlocking {
var text = it.text.toString()
var color = it.color
var font = it.font
var size = it.size
var x = it.x
var y = it.y
setTextSticker(text, Color.parseColor(color), size!!, x!!, y!!)
}
}
}
Here are my two methods where main computation is taking place:
fun setimagestickers(path:String,x:Int,y:Int,w:Int,h:Int){
Glide.with(this#NewStickerActivity).asBitmap().timeout(6000000).load(path).into(object : CustomTarget<Bitmap>() {
override fun onLoadCleared(placeholder: Drawable?) {
}
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
var size: ViewGroup.LayoutParams
var bmp1 = resource
size = UiHelper.getHeightWidth(this#NewStickerActivity, (w).toInt(), (h).toInt())
var resizedBitmap = Bitmap.createScaledBitmap(bmp1, size.width, size.height, false)
var drawable = BitmapDrawable(resources, resizedBitmap)
var dsImageSticker = DrawableSticker(drawable)
dsImageSticker.setTag("ImageSticker")
var pm: List<Int>
if (density > 3.0) {
pm = UiHelper.getmargins(this#NewStickerActivity, (x).toInt(), (y).toInt())
} else {
pm = UiHelper.getmargins(this#NewStickerActivity, (x).toInt(), (y).toInt())
}
Log.i("Hmmm:", pm.get(0).toFloat().toString() + "::" + pm.get(1).toFloat().toString())
stickerView.addStickerAndSetMatrix1(
dsImageSticker,
pm.get(0).toFloat(),
pm.get(1).toFloat()
)
}
})
}
fun setTextSticker(text: String, color: Int,size: Int, x: Int, y: Int){
val bmp1: Bitmap
val drawable: Drawable
var l: List<Int>
if (density > 3.0) {
l = UiHelper.getmargins(this#NewStickerActivity, (x).toInt(), (y * 1.07).toInt())
} else {
l = UiHelper.getmargins(this#NewStickerActivity, x.toInt(), y.toInt())
}
//var tf = Typeface.createFromFile(assets,"fonts/"+path)
var tf = Typeface.createFromAsset(assets, "fonts/Myriad Pro Bold SemiExtended.ttf")
bmp1 = createBitmapFromLayoutWithText(this#NewStickerActivity, size, text, color, 0f, tf, 0f, 0f, color, Gravity.LEFT)
drawable = BitmapDrawable(resources, bmp1)
var dsTextSticker = DrawableSticker(drawable)
dsTextSticker.setTag("textSticker")
Log.i("Hmmm:", l.get(0).toFloat().toString() + "::" + l.get(1).toFloat().toString())
/*if (rotate) {
stic.addStickerAndSetrotate(
dsTextSticker, rotationdegress,
l.get(0).toFloat(),
l.get(1).toFloat()
)
} else {*/
stickerView.addStickerAndSetMatrix1(
dsTextSticker,
l.get(0).toFloat(),
l.get(1).toFloat())
}
UPDATE:
I got this working without coroutines by incrementing and fetching images in a sequence:
Firstly i took a Int and then kept incrementing until it reached list size here is my code:
Firstly i did this :
var i = 0
setimagestickers(imagestickers.get(i).path!!, imagestickers.get(i).x!!, imagestickers.get(i).y!!, imagestickers.get(i).width!!, imagestickers.get(i).height!!)
After that inside on resource ready did the trick!!
Glide.with(this#NewStickerActivity).asBitmap().timeout(6000000).load(path).into(object : CustomTarget<Bitmap>() {
override fun onLoadCleared(placeholder: Drawable?) {
}
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
var size: ViewGroup.LayoutParams
var bmp1 = resource
size = UiHelper.getHeightWidth(this#NewStickerActivity, (w).toInt(), (h).toInt())
var resizedBitmap = Bitmap.createScaledBitmap(bmp1, size.width, size.height, false)
var drawable = BitmapDrawable(resources, resizedBitmap)
var dsImageSticker = DrawableSticker(drawable)
dsImageSticker.setTag("ImageSticker")
var pm: List<Int>
if (density > 3.0) {
pm = UiHelper.getmargins(this#NewStickerActivity, (x).toInt(), (y).toInt())
} else {
pm = UiHelper.getmargins(this#NewStickerActivity, (x).toInt(), (y).toInt())
}
Log.i("Hmmm:", pm.get(0).toFloat().toString() + "::" + pm.get(1).toFloat().toString())
stickerView.addStickerAndSetMatrix1(
dsImageSticker,
pm.get(0).toFloat(),
pm.get(1).toFloat()
)
i++
if(i < imagestickers.size){
setimagestickers(imagestickers.get(i).path!!, imagestickers.get(i).x!!, imagestickers.get(i).y!!, imagestickers.get(i).width!!, imagestickers.get(i).height!!)
}
else{
if(textstickers.isNullOrEmpty()){
loader!!.hide()
}
else {
setTextSticker(textstickers.get(j).text!!, Color.parseColor(textstickers.get(j).color), textstickers.get(j).size!!, textstickers.get(j).x!!, textstickers.get(j).y!!)
}
}
}
})
However i'm still wondering how can i solve it with coroutines rather than this approach!!!
Basically, you're making async Glide calls without suspending the coroutine until they are done. The first thing you must change is introduce a suspend fun getImageSticker():
suspend fun getSticker(path: String): Bitmap =
suspendCancellableCoroutine { continuation -> Glide
.with(this#NewStickerActivity)
.asBitmap()
.timeout(6000000)
.load(path)
.into(object : CustomTarget<Bitmap>() {
override fun onResourceReady(resource: Bitmap, x: Transition<in Bitmap>?) {
continuation.resume(resource)
}
})
}
Note that this returns a Bitmap. Now you can just call it as if it was a regular, blocking function, and use its result from the caller side:
suspend fun setimagestickers(path: String, x: Int, y: Int, w: Int, h: Int) {
val resource = getSticker(path)
var size: ViewGroup.LayoutParams
var bmp1 = resource
size = UiHelper.getHeightWidth(this#NewStickerActivity, (w).toInt(), (h).toInt())
var resizedBitmap = Bitmap.createScaledBitmap(bmp1, size.width, size.height, false)
}
Now, if you want to parallelize setimagestickers calls, launch them in a loop:
launch {
imagestickers.map {
launch {
var image = it.path
var x = it.x
var y = it.y
image!!.log()
setimagestickers(image!!, x!!, y!!, it.width!!, it.height!!)
}
}.joinAll()
textstickers.forEach {
...
}
}
None of your code will benefit from runBlocking or the coroutines you launch.
If you are aiming to simply process each of your Image type objects in paralellel due to the long running nature of each task, then you can do something like this:
fun main() {
runBlocking {
imagestickers.map {
launch {
var image = it.path
var x = it.x
var y = it.y
image!!.log()
setimagestickers(image!!, x!!, y!!, it.width!!, it.height!!)
}
}.joinAll()
textstickers.map {
launch {
var text = it.text.toString()
var color = it.color
var font = it.font
var size = it.size
var x = it.x
var y = it.y
setTextSticker(text, Color.parseColor(color), size!!, x!!, y!!)
}
}.joinAll()
}
}
Why I removed some of the stuff you were doing:
The nested runBlocking calls you had did nothing. `runBlocking means "block until whatever is inside this block is finished". Your for loops were not async, and would run until they were done anyway.
The first coroutine launch you had did nothing as well. You launched it, and then immediately called join. This means that you launch a coroutine, but then immediately wait until it is finished before proceeding.
What this does:
Rather than loops, we map each image object to a list of jobs for concurrently running coroutines.
We then wait for every job to finish in each list before proceeding to the next list.
Please Note: It does not look like any of your code benefits from having non-blocking IO, in which case, this parralelism will be limited to the amount of threads you have. That fetching of the image will block the thread it is on (regardless of using coroutines) unless you use a non-blocking library that properly yields while it fetches your image.

LibGDX Box2D Physics Direction Inverted

For some reason in my world physics direction is inverted.I mean -9.8 gravity is acting like 9.8 gravity so objects are going up.Horizontal velocity is also inverted.Something must be wrong with x,y coordinate system.
world = World(Vector2(0f,-9.8f),false) pushes objects up
is there a problem with PPM ?
package com.mygdx.game
import com.badlogic.gdx.ApplicationAdapter
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.Input
import com.badlogic.gdx.graphics.GL20
import com.badlogic.gdx.math.Vector2
import com.badlogic.gdx.physics.box2d.*
import com.badlogic.gdx.graphics.OrthographicCamera
import com.badlogic.gdx.math.Vector3
import com.badlogic.gdx.physics.box2d.World
import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer
class MyGdxGame : ApplicationAdapter() {
val PPM = 32f
private lateinit var camera : OrthographicCamera
private lateinit var world :World
private lateinit var player:Body
private lateinit var platform:Body
private lateinit var b2dr:Box2DDebugRenderer
override fun create() {
var w = Gdx.graphics.width
var h = Gdx.graphics.height
camera = OrthographicCamera()
camera.setToOrtho(false, w / 2f, h / 2f)
world = World(Vector2(0f,-9.8f),false)
b2dr = Box2DDebugRenderer()
player = createBox(2f,10f,32,32,false)
platform = createBox(0f,0f,64,32,true)
}
override fun render() {
update(Gdx.graphics.deltaTime)
Gdx.gl.glClearColor(0f, 0f, 0f, 1f)
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)
b2dr.render(world,camera.combined.scl(PPM))
}
override fun resize(width: Int, height: Int) {
camera.setToOrtho(false, width / 2f, height / 2f)
}
override fun dispose() {
world.dispose()
b2dr.dispose()
}
fun update(delta:Float){
world.step(1/60f,6,2)
inputUpdate(delta)
cameraUpdate(delta)
}
fun inputUpdate(delta:Float){
var horizontalForce = 0f;
if(Gdx.input.isKeyPressed(Input.Keys.LEFT)){
horizontalForce -=1
}
if(Gdx.input.isKeyPressed(Input.Keys.RIGHT)){
horizontalForce +=1
}
if(Gdx.input.isKeyJustPressed(Input.Keys.UP)){
player.applyForceToCenter(0f,300f,false)
}
player.setLinearVelocity(horizontalForce*5,player.linearVelocity.y)
}
fun cameraUpdate(delta:Float){
var position:Vector3 = camera.position
position.x = player.position.x * PPM // if you get box2d units multiply with ppm
position.y = player.position.y * PPM
camera.position.set(position)
camera.update()
}
fun createBox(x:Float,y:Float,width:Int,height:Int,isStatic:Boolean):Body{
var pBody:Body
var def:BodyDef
def = BodyDef()
if(isStatic){
def.type = BodyDef.BodyType.StaticBody
}
else{
def.type = BodyDef.BodyType.DynamicBody
}
def.position.set(x/PPM,y/PPM)
def.fixedRotation = true
pBody = world.createBody(def)
var shape:PolygonShape = PolygonShape()
shape.setAsBox(width/2/PPM,height/2/PPM) // takes from center this is 32x32 box
pBody.createFixture(shape,1.0f)
shape.dispose()//clean it
return pBody
}
}
It's all correct with box2d and your code.
-9.81 pushes objects up and 9.81 pull objects down.
In the World constructor, you set the gravity of the world.
And Gravity is a natural phenomenon by which all things with mass or
energy—including planets, stars, galaxies, and even light—are brought
toward (or gravitate toward) one another.
Basic Gravity is the force with which object attract each other.
So on Earth gravity is the force with which objects are attracted to the center of the earth.
On Earth, we have a gravity of ~ 9.81 means all Objects will pull to the center of the earth with a force of ~ 9.81 m/s².
Finally is it logical that if gravity is a force which pulls objects down that -gravity will push objects up.
On Earth, we have a gravity of ~ 9.81 not a gravity of ~ -9.81.
If it were, we would all end up outside in the universe.

How to initialize a variable in Kotlin with an Interfaced type?

I'm brand new to Kotlin. I wanted to try the create-react-kotlin-app since I'm a react developer currently and I want to explore Kotlin.
I'm having a tough time instaniating my variables. I'm trying to do this:
fun main(args: Array<String>) {
val rootDiv = document.getElementById("root")
val gm : GameUiProps
gm.numPlayers = 2
gm.cardArray = arrayOf("Card to be dealt", "Cards to be Dealt")
gm.playerArray = arrayOf("Player 1", "Player 2")
RBuilder.render(rootDiv) {
GameUi(gm)
}
}
My gm variable isn't being initialized and I can't figure how how to do it.
I need to initiazle my GameUi component with props, but I can't figure out how to do that.
So GameUiProps comes from my component
interface GameUiProps : RProps {
var numPlayers: Int
var playerArray: Array<String>
var cardArray: Array<String>
}
class GameUi(props: GameUiProps) : RComponent<GameUiProps, RState>(props) {
private fun RBuilder.createHands() {
var numPlayers = props.numPlayers
val handArray: ArrayList<Unit> = ArrayList()
while (numPlayers > 0) {
handArray.add(
handUi(
props.playerArray[numPlayers],
props.cardArray[numPlayers]
)
)
numPlayers--
}
}
override fun RBuilder.render() {
div {
h1("sweet") {
+"RKT"
}
div {
createHands()
}
}
}
}
Your GameUiProps is just an interface, so you cannot instantiate it directly.
What you have (val gm: GameUiProps) is just field declaration without assigning anything to it. You obviously cannot access nor write any properties to it.
You need to either create a simple implementation with constructor, or an anonymous object.
Class that declares all interface fields in the constructor:
class GameUiPropsImpl(
override var numPlayers: Int,
override var playerArray: Array<String>,
override var cardArray: Array<String>) : GameUiProps
then in your fun main:
val gm = GameUiPropsImpl(2, arrayOf("Player 1", "Player 2"), arrayOf("Card to be dealt", "Cards to be Dealt"))
Or using an anonymous class implementing the interface:
val gm = object: GameUiProps{
override var numPlayers = 2
override var playerArray = arrayOf("Player 1", "Player 2")
override var cardArray = arrayOf("Card to be dealt", "Cards to be Dealt")
}
It has been a while, but for anyone who might need this, you just need to assign the variable gm to an object of type GameUiProps. Below is what I would have done.
val gm = object : GameUiProps{
override var numPlayers = 2
override var cardArray = arrayOf("Card to be dealt", "Cards to be Dealt")
override var playerArray = arrayOf("Player 1", "Player 2")
}
Hope it helps!