Example character level animations.

This commit is contained in:
2023-01-18 23:53:39 -08:00
parent 602151d567
commit 2b1b258065
29 changed files with 388 additions and 44 deletions

View File

@ -15,12 +15,27 @@ namespace Dawn {
template<typename T>
struct SimpleAnimation : public Animation {
protected:
/**
* Function for subclasses to be "notified" when the value has been
* modified.
*/
virtual void onValueModified() {
}
public:
easefunction_t *easing = &easeLinear;
T *modifies;
std::vector<struct SimpleKeyframe<T>> keyframes;
/**
* Constructs a new Simple Animation instance.
*
* @param modifies Pointer to the value that will be modified.
*/
SimpleAnimation(T *modifies) {
assertNotNull(modifies);
this->modifies = modifies;
}
@ -31,6 +46,8 @@ namespace Dawn {
* @param value Value at this given time.
*/
void addKeyframe(float_t time, T value) {
assertTrue(time >= 0);
struct SimpleKeyframe<T> keyframe;
keyframe.time = time;
keyframe.value = value;
@ -83,6 +100,17 @@ namespace Dawn {
this->addSequentialKeyframes(0, frameTime, start, end, 1);
}
/**
* Immediately sets the value, bypassing keyframes and ticks. Useful for
* setting an initial value.
*
* @param value Value to set.
*/
void setValue(T value) {
*modifies = value;
this->onValueModified();
}
void tick(float_t delta) override {
if(this->finished) return;
@ -106,6 +134,7 @@ namespace Dawn {
if(keyframeCurrent != nullptr && keyframeNext == nullptr) {
// "End of animation"
*this->modifies = keyframeCurrent->value;
this->onValueModified();
} else if(keyframeNext != nullptr) {
T oldValue;
float_t oldTime;
@ -127,6 +156,13 @@ namespace Dawn {
*this->modifies = oldValue + (
(keyframeNext->value - oldValue) * keyframeDelta
);
this->onValueModified();
}
// First possible frame? I think this can be done cleaner
if(this->time == 0 && keyframeCurrent->time == 0) {
*this->modifies = keyframeCurrent->value;
this->onValueModified();
}
// Update time.

View File

@ -0,0 +1,51 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "SimpleAnimation.hpp"
namespace Dawn {
template<typename T, class I>
struct SimpleCallbackAnimation : public SimpleAnimation<T> {
protected:
I *instance;
void (I::*callback)(T arg);
T value;
/**
* Internally invoke the function that this animation is owning.
*/
void invoke() {
assertNotNull(this->instance);
((*this->instance).*(this->callback))(this->value);
}
void onValueModified() override {
SimpleAnimation::onValueModified();
this->invoke();
}
public:
/**
* Construct a new Simple Function Animation object
*/
SimpleCallbackAnimation() :
SimpleAnimation(&value)
{
}
/**
* Sets the callback for the animation to use.
*
* @param instance Instance of the object that has the callback.
* @param callback Callback method to be invoked.
*/
void setCallback(I *instance, void (I::*callback)(T arg)) {
assertNotNull(instance);
this->instance = instance;
this->callback = callback;
}
};
}

View File

@ -15,9 +15,17 @@ AnimationController::AnimationController(SceneItem *item) :
}
void AnimationController::addAnimation(Animation *animation) {
assertNotNull(animation);
this->animations.push_back(animation);
}
void AnimationController::onSceneUpdate() {
if(this->animation == nullptr) return;
this->animation->tick(this->getGame()->timeManager.delta);
auto it = this->animations.begin();
while(it != this->animations.end()) {
(*it)->tick(this->getGame()->timeManager.delta);
++it;
}
}
void AnimationController::onStart() {

View File

@ -10,13 +10,14 @@
namespace Dawn {
class AnimationController : public SceneItemComponent {
private:
std::vector<Animation*> animations;
void onSceneUpdate();
public:
Animation *animation = nullptr;
AnimationController(SceneItem *item);
void addAnimation(Animation *animation);
void onStart() override;
void onDispose() override;
};

View File

@ -10,5 +10,19 @@ using namespace Dawn;
VisualNovelCharacter::VisualNovelCharacter(SceneItem *item) :
SceneItemComponent(item)
{
}
void VisualNovelCharacter::setOpacity(float_t opacity) {
auto interface = this->item->getComponent<SimpleTexturedShaderInterface>();
assertNotNull(interface);
auto color = interface->getColor();
color.a = opacity;
interface->setColor(color);
}
float_t VisualNovelCharacter::getOpacity() {
auto interface = this->item->getComponent<SimpleTexturedShaderInterface>();
assertNotNull(interface);
auto color = interface->getColor();
return color.a;
}

View File

@ -5,10 +5,12 @@
#pragma once
#include "scene/SceneItemComponent.hpp"
#include "scene/components/display/shader/SimpleTexturedShaderInterface.hpp"
namespace Dawn {
class VisualNovelCharacter : public SceneItemComponent {
protected:
SimpleTexturedShaderInterface *shaderInterface = nullptr;
public:
std::string nameKey = "character.unknown";
@ -20,5 +22,11 @@ namespace Dawn {
* @param item Item that this component belongs to.
*/
VisualNovelCharacter(SceneItem *item);
SimpleTexturedShaderInterface * getShaderInterface();
void setOpacity(float_t opacity);
float_t getOpacity();
};
}

View File

@ -6,9 +6,12 @@
# Sources
target_sources(${DAWN_TARGET_NAME}
PRIVATE
VisualNovelAnimationEvent.cpp
VisualNovelFadeEvent.cpp
VisualNovelPauseEvent.cpp
VisualNovelTextboxEvent.cpp
VisualNovelChangeSimpleBackgroundEvent.cpp
)
)
# Subdirs
add_subdirectory(animation)
add_subdirectory(characters)
add_subdirectory(timing)

View File

@ -19,7 +19,7 @@ VisualNovelFadeEvent::VisualNovelFadeEvent(
this->duration = duration;
this->simpleAnimation.easing = ease;
}
void VisualNovelFadeEvent::onStart(IVisualNovelEvent *previous) {
VisualNovelSimpleAnimationEvent::onStart(previous);

View File

@ -4,7 +4,7 @@
// https://opensource.org/licenses/MIT
#pragma once
#include "VisualNovelSimpleAnimationEvent.hpp"
#include "visualnovel/events/animation/VisualNovelSimpleAnimationEvent.hpp"
namespace Dawn {
class VisualNovelFadeEvent : public VisualNovelSimpleAnimationEvent<float_t> {

View File

@ -0,0 +1,10 @@
# Copyright (c) 2022 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DAWN_TARGET_NAME}
PRIVATE
VisualNovelAnimationEvent.cpp
)

View File

@ -0,0 +1,24 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "VisualNovelAnimationEvent.hpp"
#include "display/animation/SimpleCallbackAnimation.hpp"
namespace Dawn {
template<typename T, class I>
class VisualNovelSimpleCallbackAnimationEvent :
public VisualNovelAnimationEvent
{
public:
struct SimpleCallbackAnimation<T, I> callbackAnimation;
VisualNovelSimpleCallbackAnimationEvent(VisualNovelManager *man) :
VisualNovelAnimationEvent(man)
{
this->animation = &callbackAnimation;
}
};
}

View File

@ -0,0 +1,10 @@
# Copyright (c) 2022 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DAWN_TARGET_NAME}
PRIVATE
VisualNovelFadeCharacterEvent.cpp
)

View File

@ -0,0 +1,29 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "VisualNovelFadeCharacterEvent.hpp"
using namespace Dawn;
VisualNovelFadeCharacterEvent::VisualNovelFadeCharacterEvent(
VisualNovelManager *man,
VisualNovelCharacter *character,
bool_t fadeIn,
easefunction_t *ease,
float_t duration
) : VisualNovelSimpleCallbackAnimationEvent<float_t, VisualNovelCharacter>(man)
{
this->callbackAnimation.easing = ease;
this->callbackAnimation.setCallback(
character, &VisualNovelCharacter::setOpacity
);
if(fadeIn) {
this->callbackAnimation.addKeyframe(0.0f, 0.0f);
this->callbackAnimation.addKeyframe(duration, 1.0f);
} else {
this->callbackAnimation.addKeyframe(0.0f, 1.0f);
this->callbackAnimation.addKeyframe(duration, 0.0f);
}
}

View File

@ -0,0 +1,24 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "visualnovel/events/animation/VisualNovelSimpleCallbackAnimationEvent.hpp"
#include "visualnovel/components/VisualNovelCharacter.hpp"
#include "scene/components/display/Material.hpp"
namespace Dawn {
class VisualNovelFadeCharacterEvent :
public VisualNovelSimpleCallbackAnimationEvent<float_t,VisualNovelCharacter>
{
public:
VisualNovelFadeCharacterEvent(
VisualNovelManager *man,
VisualNovelCharacter *character,
bool_t fadeIn,
easefunction_t *ease,
float_t duration
);
};
}

View File

@ -0,0 +1,11 @@
# Copyright (c) 2022 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DAWN_TARGET_NAME}
PRIVATE
VisualNovelBatchEvent.cpp
VisualNovelPauseEvent.cpp
)

View File

@ -0,0 +1,66 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "VisualNovelBatchEvent.hpp"
using namespace Dawn;
VisualNovelBatchEvent::VisualNovelBatchEvent(
VisualNovelManager *man,
std::vector<IVisualNovelEvent*> events
) : IVisualNovelEvent(man) {
this->activeEvents = events;
}
void VisualNovelBatchEvent::onStart(IVisualNovelEvent *previous) {
auto it = this->activeEvents.begin();
while(it != this->activeEvents.end()) {
auto evt = *it;
evt->start(previous);
++it;
}
}
bool_t VisualNovelBatchEvent::onUpdate() {
bool_t result;
auto it = this->activeEvents.begin();
while(it != this->activeEvents.end()) {
auto evt = *it;
result = evt->update();
if(result) {
++it;
continue;
}
auto subNext = evt->end();
// In future I may remove this and instead immediately queue the next thing.
assertNull(subNext);
it = this->activeEvents.erase(it);
this->inactiveEvents.push_back(evt);
}
return this->activeEvents.size() > 0;
}
void VisualNovelBatchEvent::onEnd() {
}
VisualNovelBatchEvent::~VisualNovelBatchEvent() {
auto itActive = this->activeEvents.begin();
while(itActive != this->activeEvents.end()) {
auto evt = *itActive;
delete evt;
++itActive;
}
auto itInactive = this->inactiveEvents.begin();
while(itInactive != this->inactiveEvents.end()) {
auto evt = *itInactive;
delete evt;
++itInactive;
}
}

View File

@ -0,0 +1,27 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "visualnovel/VisualNovelManager.hpp"
namespace Dawn {
class VisualNovelBatchEvent : public IVisualNovelEvent {
protected:
std::vector<IVisualNovelEvent*> activeEvents;
std::vector<IVisualNovelEvent*> inactiveEvents;
void onStart(IVisualNovelEvent *previous) override;
bool_t onUpdate() override;
void onEnd() override;
public:
VisualNovelBatchEvent(
VisualNovelManager *man,
std::vector<IVisualNovelEvent*> events
);
~VisualNovelBatchEvent();
};
}

View File

@ -11,7 +11,7 @@
#include "scene/components/audio/AudioListener.hpp"
#include "visualnovel/VisualNovelManager.hpp"
#include "visualnovel/events/VisualNovelTextboxEvent.hpp"
#include "visualnovel/events/VisualNovelPauseEvent.hpp"
#include "visualnovel/events/timing/VisualNovelPauseEvent.hpp"
#include "visualnovel/events/VisualNovelFadeEvent.hpp"
#include "visualnovel/events/VisualNovelCAllbackEvent.hpp"
#include "visualnovel/events/VisualNovelChangeSimpleBackgroundEvent.hpp"

View File

@ -95,7 +95,7 @@ void AudioSource::onStart() {
alGenBuffers((ALuint)AUDIO_SOURCE_BUFFER_COUNT, this->buffers);
// Set up the source positions
glm::vec3 position = this->transform->getLocalPosition();
glm::vec3 position = this->transform->getWorldPosition();
alSource3f(this->source, AL_POSITION, position.x, position.y, position.z);
alSource3f(this->source, AL_VELOCITY, 0, 0, 0);

View File

@ -19,4 +19,8 @@ void SimpleTexturedShaderInterface::setTexture(Texture *texture) {
void SimpleTexturedShaderInterface::setColor(struct Color color) {
this->getMaterial()->colorValues[this->getShader()->paramColor] = color;
}
struct Color SimpleTexturedShaderInterface::getColor() {
return this->getMaterial()->colorValues[this->getShader()->paramColor];
}

View File

@ -32,5 +32,12 @@ namespace Dawn {
* @param color Color to be used.
*/
void setColor(struct Color color);
/**
* Returns the current color from the shader.
*
* @return Current color.
*/
struct Color getColor();
};
}

View File

@ -32,11 +32,11 @@ tool_texture(texture_test texture_test.png)
tool_language(language_en ${DIR_GAME_ASSETS}/locale/en.csv)
tool_language(language_jp ${DIR_GAME_ASSETS}/locale/jp.csv)
tool_tileset(tileset_death texture_death ${DIR_GAME_ASSETS}/characters/death/sheet.png 1 2)
tool_tileset(tileset_death texture_death ${DIR_GAME_ASSETS}/characters/death/sheet.png 1 3)
tool_truetype(truetype_alice ${DIR_GAME_ASSETS}/font/Alice-Regular.ttf truetype_alice 2048 2048 120)
tool_audio(audio_test borrowed/sample.wav)
tool_audio(audio_test borrowed/sample_long.wav)
add_dependencies(${DAWN_TARGET_NAME}
language_en

View File

@ -17,6 +17,7 @@ namespace Dawn {
public:
VisualNovelCharacter *vnCharacter;
SimpleTexturedShaderInterface *shaderInterface;
AnimationController *animation;
static std::vector<Asset*> prefabAssets(AssetManager *assMan) {
return std::vector<Asset*>{
@ -25,7 +26,10 @@ namespace Dawn {
};
}
DeathPrefab(Scene *scene, sceneitemid_t id) : SceneItemPrefab(scene, id){}
DeathPrefab(Scene *scene, sceneitemid_t id) : SceneItemPrefab(scene, id)
{
}
void prefabInit(AssetManager *man) override {
auto textureAsset = man->get<TextureAsset>("texture_death");
@ -34,17 +38,18 @@ namespace Dawn {
auto meshRenderer = this->addComponent<MeshRenderer>();
auto material = this->addComponent<Material>();
auto meshHost = this->addComponent<MeshHost>();
auto animation = this->addComponent<AnimationController>();
auto tiledSprite = this->addComponent<TiledSprite>();
tiledSprite->setTilesetAndSize(&tilesetAsset->tileset);
tiledSprite->setTile(0);
vnCharacter = this->addComponent<VisualNovelCharacter>();
vnCharacter->nameKey = "character.death.name";
shaderInterface = this->addComponent<SimpleTexturedShaderInterface>();
shaderInterface->setTexture(&textureAsset->texture);
vnCharacter = this->addComponent<VisualNovelCharacter>();
vnCharacter->nameKey = "character.death.name";
animation = this->addComponent<AnimationController>();
auto tiledSprite = this->addComponent<TiledSprite>();
tiledSprite->setTilesetAndSize(&tilesetAsset->tileset);
tiledSprite->setTile(0);
this->transform.setLocalPosition(glm::vec3(0, 0, 0));
}

View File

@ -9,34 +9,23 @@
#include "prefabs/characters/DeathPrefab.hpp"
#include "scene/components/audio/AudioListener.hpp"
#include "scene/components/audio/AudioSource.hpp"
#include "visualnovel/events/characters/VisualNovelFadeCharacterEvent.hpp"
#include "visualnovel/events/timing/VisualNovelBatchEvent.hpp"
namespace Dawn {
class Scene_1 : public PixelVNScene {
protected:
DeathPrefab *death;
AudioSource *source;
void onFinished() {
std::cout << "Finished" << std::endl;
source->rewind();
}
DeathPrefab *death2;
void vnStage() override {
PixelVNScene::vnStage();
this->death = DeathPrefab::create(this);
// this->death->vnCharacter.setOpacity(0);
this->death->vnCharacter->setOpacity(0);
auto sourceItem = this->createSceneItem();
source = sourceItem->addComponent<AudioSource>();
source->transform->setLocalPosition(glm::vec3(1, 0, 0));
source->eventFinished.addListener(this, &Scene_1::onFinished);
auto audio = this->game->assetManager.get<AudioAsset>("audio_test");
source->setAudioData(audio);
source->loop = true;
source->state = AUDIO_SOURCE_STATE_PLAYING;
this->death2 = DeathPrefab::create(this);
this->death2->transform.setLocalPosition(glm::vec3(100, 0, 0));
}
void onSceneEnded() {
@ -51,12 +40,29 @@ namespace Dawn {
IVisualNovelEvent * getVNEvent() override {
auto start = new VisualNovelPauseEvent(vnManager, 1.0f);
start
// ->then(new VisualNovelTextboxEvent(vnManager, nullptr, "scene.1.1"))
// ->then(new VisualNovelCallbackEvent<Scene_1>(vnManager, this, &Scene_1::onSceneEnded))
->then(new VisualNovelBatchEvent(
vnManager,
std::vector<IVisualNovelEvent*>{
new VisualNovelFadeCharacterEvent(
vnManager,
this->death->vnCharacter,
true,
&easeLinear,
2.0f
),
new VisualNovelFadeCharacterEvent(
vnManager,
this->death2->vnCharacter,
false,
&easeLinear,
2.0f
)
}
))
->then(new VisualNovelTextboxEvent(vnManager, nullptr, "scene.1.1"))
->then(new VisualNovelCallbackEvent<Scene_1>(vnManager, this, &Scene_1::onSceneEnded))
;
return start;
}