Сущности-предметы!

This commit is contained in:
DBotThePony 2022-12-30 12:27:18 +07:00
parent b9b4140832
commit a0705472f8
Signed by: DBot
GPG Key ID: DCC23B5715498507
8 changed files with 155 additions and 9 deletions

View File

@ -6,6 +6,7 @@ import org.lwjgl.glfw.GLFW.glfwSetWindowShouldClose
import ru.dbotthepony.kstarbound.client.StarboundClient
import ru.dbotthepony.kstarbound.io.*
import ru.dbotthepony.kstarbound.world.ChunkPos
import ru.dbotthepony.kstarbound.world.entities.ItemEntity
import ru.dbotthepony.kstarbound.world.entities.PlayerEntity
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
import java.io.ByteArrayInputStream
@ -160,6 +161,13 @@ fun main() {
println("$find $set $parse")
//client.world!!.parallax = Starbound.parallaxAccess["garden"]
for (i in 0 .. 16) {
val item = ItemEntity(client.world!!, Starbound.itemAccess["brain"]!!)
item.position = Vector2d(600.0 + 16.0 + i, 721.0 + 48.0)
item.spawn()
}
}
//ent.position += Vector2d(y = 14.0, x = -10.0)

View File

@ -494,7 +494,9 @@ class GLStateTracker {
return@computeIfAbsent named2DTextures[missingTexturePath]!!
}
return@computeIfAbsent newTexture(path).upload(Starbound.readDirect(path)).generateMips()
return@computeIfAbsent newTexture(path).upload(Starbound.readDirect(path)).generateMips().also {
it.textureMagFilter = GL_NEAREST
}
}
}

View File

@ -6,7 +6,9 @@ import ru.dbotthepony.kstarbound.client.ClientChunk
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers
import ru.dbotthepony.kstarbound.client.gl.vertex.quadRotatedZ
import ru.dbotthepony.kstarbound.client.render.entity.ItemRenderer
import ru.dbotthepony.kstarbound.world.entities.Entity
import ru.dbotthepony.kstarbound.world.entities.ItemEntity
import ru.dbotthepony.kstarbound.world.entities.projectile.Projectile
import ru.dbotthepony.kvector.matrix.Matrix4fStack
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
@ -46,6 +48,7 @@ open class EntityRenderer(val state: GLStateTracker, val entity: Entity, open va
fun getRender(state: GLStateTracker, entity: Entity, chunk: ClientChunk? = null): EntityRenderer {
return when (entity) {
is Projectile -> ProjectileRenderer(state, entity, chunk)
is ItemEntity -> ItemRenderer(state, entity, chunk)
else -> EntityRenderer(state, entity, chunk)
}
}
@ -57,10 +60,6 @@ open class ProjectileRenderer(state: GLStateTracker, entity: Projectile, chunk:
private val texture = state.loadNamedTextureSafe(def.image.texture)
private val animator = FrameSetAnimator(def.image, def.animationCycle, entity.def.animationLoops)
init {
texture.textureMagFilter = GL_NEAREST
}
override fun render(stack: Matrix4fStack) {
state.shaderVertexTexture.use()
state.shaderVertexTexture.transform.set(stack.last)

View File

@ -0,0 +1,39 @@
package ru.dbotthepony.kstarbound.client.render.entity
import org.lwjgl.opengl.GL46
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf
import ru.dbotthepony.kstarbound.client.ClientChunk
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers
import ru.dbotthepony.kstarbound.client.gl.vertex.quadRotatedZ
import ru.dbotthepony.kstarbound.client.render.EntityRenderer
import ru.dbotthepony.kstarbound.world.entities.ItemEntity
import ru.dbotthepony.kvector.matrix.Matrix4fStack
class ItemRenderer(state: GLStateTracker, entity: ItemEntity, chunk: ClientChunk?) : EntityRenderer(state, entity, chunk) {
private val def = entity.def
private val texture = def.inventoryIcon?.let(state::loadNamedTextureSafe)
override fun render(stack: Matrix4fStack) {
if (texture == null)
return
state.shaderVertexTexture.use()
state.shaderVertexTexture.transform.set(stack.last)
state.activeTexture = 0
state.shaderVertexTexture["_texture"] = 0
texture.bind()
val builder = state.flat2DTexturedQuads.small
builder.begin()
val width = (texture.width / PIXELS_IN_STARBOUND_UNITf) / 2f
val height = (texture.height / PIXELS_IN_STARBOUND_UNITf) / 2f
builder.quadRotatedZ(-width, -height, width, height, 5f, 0f, 0f, entity.movement.angle, QuadTransformers.uv(0f, 1f, 1f, 0f))
builder.upload()
builder.draw()
}
}

View File

@ -160,6 +160,8 @@ abstract class Entity(override val world: World<*, *>) : IEntity {
if (chunk == null) {
world.orphanedEntities.add(this)
}
movement.onSpawnedInWorld()
}
override fun remove() {
@ -178,8 +180,15 @@ abstract class Entity(override val world: World<*, *>) : IEntity {
/**
* Контроллер перемещения данной сущности
*
* Контроллер перемещения реализует физические интерфейсы для KBox2D и отвечает за
* физику сущности в мире.
*/
abstract override val movement: MovementController<*>
/**
* Внутренний блок "раздумья" сущности, вызывается на каждом тике мира
*/
protected abstract fun thinkAI(delta: Double)
/**

View File

@ -0,0 +1,54 @@
package ru.dbotthepony.kstarbound.world.entities
import ru.dbotthepony.kbox2d.api.ContactImpulse
import ru.dbotthepony.kbox2d.api.FixtureDef
import ru.dbotthepony.kbox2d.api.Manifold
import ru.dbotthepony.kbox2d.collision.shapes.PolygonShape
import ru.dbotthepony.kbox2d.dynamics.contact.AbstractContact
import ru.dbotthepony.kstarbound.defs.item.ItemDefinition
import ru.dbotthepony.kstarbound.world.World
class ItemEntity(world: World<*, *>, val def: ItemDefinition) : Entity(world) {
override val movement = object : MovementController<ItemEntity>(this) {
override fun beginContact(contact: AbstractContact) {
// тут надо код подбора предмета игроком, если мы начинаем коллизию с окружностью подбора
}
override fun endContact(contact: AbstractContact) {
}
override fun postSolve(contact: AbstractContact, impulse: ContactImpulse) {
}
override fun preSolve(contact: AbstractContact, oldManifold: Manifold) {
if (contact.fixtureA.userData is ItemEntity && contact.fixtureB.userData is ItemEntity)
contact.isEnabled = false
}
override fun onSpawnedInWorld() {
super.onSpawnedInWorld()
// все предметы в мире являются коробками
val fixture = FixtureDef(
shape = PolygonShape().also { it.setAsBox(1.0, 1.0) },
restitution = 0.0,
friction = 1.0,
density = 0.3,
)
fixture.userData = this@ItemEntity
body.createFixture(fixture)
// предметы не могут поворачиваться в мире, всегда падают плашмя
body.isFixedRotation = true
}
}
override fun thinkAI(delta: Double) {
// TODO: деспавнинг?
// просто, как бы, предметы не должны уж так сильно нагружать процессор
}
}

View File

@ -17,11 +17,16 @@ abstract class MovementController<T : IEntity>(val entity: T) : IContactListener
open var position by entity::position
open var angle by entity::angle
/**
* Уничтожает данный movement controller.
*
* Вызывается изнутри [Entity.remove]
*/
open fun destroy() {
body.world.destroyBody(body)
}
protected val body by lazy {
private val bodyInit = lazy {
world.physics.createBody(BodyDef(
position = position,
angle = angle,
@ -30,10 +35,36 @@ abstract class MovementController<T : IEntity>(val entity: T) : IContactListener
))
}
/**
* Было ли создано физическое тело
*/
val bodyInitialized: Boolean
get() = bodyInit.isInitialized()
/**
* Физическое тело данного контроллера перемещения
*
* Не создаётся в мире пока к нему не обратятся
*/
protected val body by bodyInit
/**
* Вызывается изнутри [Entity], когда оно спавнится в самом мире.
*
* Так как никто не запрещает нам создавать физические тела в физическом мире
* до того, как мы появимся в самом мире, это негативно сказывается на производительности И не является корректным
* поведением.
*
* Поэтому, прикреплять фигуры к физическому телу лучше всего из данной функции.
*/
open fun onSpawnedInWorld() {
}
open val velocity get() = body.linearVelocity
/**
* Returns whenever are we contacting something below us
* Проверяет, находится ли что-либо под нами
*/
open val onGround: Boolean get() {
for (contact in body.contactEdgeIterator) {

View File

@ -77,7 +77,9 @@ abstract class AbstractProjectileMovementController(entity: Projectile, val def:
}
class BouncyPhysics(entity: Projectile, def: ConfiguredProjectile) : AbstractProjectileMovementController(entity, def) {
init {
override fun onSpawnedInWorld() {
super.onSpawnedInWorld()
body.createFixture(FixtureDef(
shape = PolygonShape().also { it.setAsBox(0.5, 0.2) },
restitution = 0.9,
@ -88,7 +90,9 @@ class BouncyPhysics(entity: Projectile, def: ConfiguredProjectile) : AbstractPro
}
class FlamePhysics(entity: Projectile, def: ConfiguredProjectile) : AbstractProjectileMovementController(entity, def) {
init {
override fun onSpawnedInWorld() {
super.onSpawnedInWorld()
body.createFixture(FixtureDef(
shape = PolygonShape().also { it.setAsBox(0.2, 0.2) },
restitution = 0.0,