All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.almasb.fxgl.entity.components.ViewComponent.kt Maven / Gradle / Ivy

There is a newer version: 21.1
Show newest version
/*
 * FXGL - JavaFX Game Library. The MIT License (MIT).
 * Copyright (c) AlmasB ([email protected]).
 * See LICENSE for details.
 */

package com.almasb.fxgl.entity.components

import com.almasb.fxgl.core.View
import com.almasb.fxgl.entity.component.Component
import com.almasb.fxgl.entity.component.CoreComponent
import javafx.beans.property.*
import javafx.event.Event
import javafx.event.EventHandler
import javafx.event.EventType
import javafx.scene.Group
import javafx.scene.Node
import javafx.scene.Parent
import javafx.scene.input.MouseEvent
import javafx.scene.transform.Rotate
import javafx.scene.transform.Scale

/**
 * Represents visual aspect of an entity.
 *
 * @author Almas Baimagambetov ([email protected])
 */
@CoreComponent
class ViewComponent : Component() {

    private val viewRoot = Group()

    // no scale or rotate is applied
    private val viewRootNoTransform = Group()
    private val devRoot = Group()

    private val updatableViews = arrayListOf()

    /**
     * This node is managed by FXGL and is part of active scene graph, do NOT modify children.
     */
    val parent: Parent = Group()

    @get:JvmName("zIndexProperty")
    val zIndexProperty = SimpleIntegerProperty(0)

    @get:JvmName("opacityProperty")
    val opacityProperty = SimpleDoubleProperty(1.0)

    var opacity: Double
        get() = opacityProperty.value
        set(value) { opacityProperty.value = value }

    var zIndex: Int
        get() = zIndexProperty.value
        set(value) { zIndexProperty.value = value }

    @get:JvmName("visibleProperty")
    val visibleProperty: BooleanProperty
        get() = parent.visibleProperty()

    /**
     * If made invisible, all events (e.g. mouse) go through the entity
     * and do not register on the entity.
     * In contrast, changing opacity to 0 still allows the entity to receive
     * events (e.g. mouse clicks).
     */
    var isVisible: Boolean
        get() = parent.isVisible
        set(value) { parent.isVisible = value }

    /**
     * @return all view children (the order is transform applied first, then without transforms)
     */
    val children: List
        get() = viewRoot.children + viewRootNoTransform.children

    init {
        viewRoot.opacityProperty().bind(opacityProperty)
        viewRootNoTransform.opacityProperty().bind(viewRoot.opacityProperty())
    }

    override fun onAdded() {
        viewRoot.translateXProperty().bind(entity.xProperty().subtract(entity.transformComponent.positionOriginXProperty()))
        viewRoot.translateYProperty().bind(entity.yProperty().subtract(entity.transformComponent.positionOriginYProperty()))
        viewRoot.translateZProperty().bind(entity.zProperty().subtract(entity.transformComponent.positionOriginZProperty()))

        viewRootNoTransform.translateXProperty().bind(viewRoot.translateXProperty())
        viewRootNoTransform.translateYProperty().bind(viewRoot.translateYProperty())
        viewRootNoTransform.translateZProperty().bind(viewRoot.translateZProperty())

        devRoot.translateXProperty().bind(viewRoot.translateXProperty())
        devRoot.translateYProperty().bind(viewRoot.translateYProperty())
        devRoot.translateZProperty().bind(viewRoot.translateZProperty())

        val scale = Scale()
        scale.xProperty().bind(entity.transformComponent.scaleXProperty())
        scale.yProperty().bind(entity.transformComponent.scaleYProperty())
        scale.zProperty().bind(entity.transformComponent.scaleZProperty())

        scale.pivotXProperty().bind(entity.transformComponent.scaleOriginXProperty())
        scale.pivotYProperty().bind(entity.transformComponent.scaleOriginYProperty())
        scale.pivotZProperty().bind(entity.transformComponent.scaleOriginZProperty())

        val rz = Rotate()
        rz.axis = Rotate.Z_AXIS
        rz.angleProperty().bind(entity.transformComponent.angleProperty())

        rz.pivotXProperty().bind(entity.transformComponent.rotationOriginXProperty())
        rz.pivotYProperty().bind(entity.transformComponent.rotationOriginYProperty())
        rz.pivotZProperty().bind(entity.transformComponent.rotationOriginZProperty())

        val ry = Rotate(0.0, Rotate.Y_AXIS)
        ry.angleProperty().bind(entity.transformComponent.rotationYProperty())

        ry.pivotXProperty().bind(entity.transformComponent.rotationOriginXProperty())
        ry.pivotYProperty().bind(entity.transformComponent.rotationOriginYProperty())
        ry.pivotZProperty().bind(entity.transformComponent.rotationOriginZProperty())

        val rx = Rotate(0.0, Rotate.X_AXIS)
        rx.angleProperty().bind(entity.transformComponent.rotationXProperty())

        rx.pivotXProperty().bind(entity.transformComponent.rotationOriginXProperty())
        rx.pivotYProperty().bind(entity.transformComponent.rotationOriginYProperty())
        rx.pivotZProperty().bind(entity.transformComponent.rotationOriginZProperty())

        viewRoot.transforms.addAll(rz, ry, rx, scale)
        devRoot.transforms.addAll(rz, ry, rx, scale)
    }

    override fun onUpdate(tpf: Double) {
        updatableViews.forEach { it.onUpdate(tpf) }
    }

    override fun onRemoved() {
        (parent as Group).children.clear()
        clearChildren()
    }

    /**
     * Register event handler for event type that occurs on this view.
     */
    fun  addEventHandler(eventType: EventType, eventHandler: EventHandler) {
        parent.addEventHandler(eventType, eventHandler)
    }

    fun addOnClickHandler(eventHandler: EventHandler) {
        addEventHandler(MouseEvent.MOUSE_CLICKED, eventHandler)
    }

    /**
     * Remove event handler for event type that occurs on this view.
     */
    fun  removeEventHandler(eventType: EventType, eventHandler: EventHandler) {
        parent.removeEventHandler(eventType, eventHandler)
    }

    fun removeOnClickHandler(eventHandler: EventHandler) {
        removeEventHandler(MouseEvent.MOUSE_CLICKED, eventHandler)
    }

    /**
     * Add a child node to this view.
     */
    @JvmOverloads fun addChild(node: Node, isTransformApplied: Boolean = true) {
        if (isTransformApplied) {
            addToGroup(viewRoot, node)
        } else {
            addToGroup(viewRootNoTransform, node)
        }

        if (node is View)
            updatableViews += node
    }

    /**
     * Remove a child from this view.
     */
    fun removeChild(node: Node) {
        removeFromGroup(viewRoot, node)
        removeFromGroup(viewRootNoTransform, node)

        if (node is View)
            updatableViews -= node
    }

    /**
     * Internal use only.
     */
    fun addDevChild(node: Node) {
        addToGroup(devRoot, node, true)
    }

    /**
     * Internal use only.
     */
    fun removeDevChild(node: Node) {
        removeFromGroup(devRoot, node)
    }

    private fun addToGroup(group: Group, child: Node, addLast: Boolean = false) {
        if (!(parent as Group).children.contains(group)) {
            if (addLast) {
                parent.children += group
            } else {
                parent.children.add(0, group)
            }
        }

        group.children += child
    }

    private fun removeFromGroup(group: Group, child: Node) {
        group.children -= child

        if (group.children.isEmpty()) {
            (parent as Group).children -= group
        }
    }

    /**
     * Remove all (with and without transforms) children.
     */
    fun clearChildren() {
        viewRoot.children.forEach {
            if (it is View) {
                updatableViews -= it

                it.dispose()
            }
        }

        viewRootNoTransform.children.forEach {
            if (it is View) {
                updatableViews -= it

                it.dispose()
            }
        }

        viewRoot.children.clear()
        viewRootNoTransform.children.clear()
    }

    override fun isComponentInjectionRequired(): Boolean = false
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy