create conversationPage
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
parent
8de6e9d771
commit
2336262c57
7 changed files with 92 additions and 76 deletions
|
@ -8,8 +8,8 @@
|
|||
type: conversation
|
||||
name: Porsche
|
||||
text: ”Hi! I haven’t seen you around before so you must be new. I’m Porsche. Why don’t you sit with me and my friends? We'll tell you about the school!”
|
||||
sprite: ""
|
||||
background: ""
|
||||
sprite: "_Porsche_normal.zip"
|
||||
background: "bg-hallway.png"
|
||||
goTo: 3
|
||||
|
||||
- id: 3
|
||||
|
|
9
src/css/conversation.css
Normal file
9
src/css/conversation.css
Normal file
|
@ -0,0 +1,9 @@
|
|||
.spriteBox {
|
||||
height: 50vh;
|
||||
width: 30vw;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.spriteBox {
|
||||
width: 75vw;
|
||||
}
|
||||
}
|
16
src/css/nameBox.css
Normal file
16
src/css/nameBox.css
Normal file
|
@ -0,0 +1,16 @@
|
|||
.nameBox {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 20vw;
|
||||
min-width: 100px;
|
||||
background-color: #8391b8;
|
||||
color: white;
|
||||
border-style: solid;
|
||||
border-color: #8391b8;
|
||||
border-width: 5px;
|
||||
border-radius: -10px;
|
||||
position: absolute;
|
||||
transform: translateY(-40px);
|
||||
z-index: 2;
|
||||
}
|
|
@ -9,13 +9,5 @@
|
|||
border-color: #8391b8;
|
||||
border-width: 5px;
|
||||
border-radius: 30px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.textBox.title {
|
||||
margin-top: 50vh;
|
||||
}
|
||||
.textBox.title.small {
|
||||
margin-top: 50vh;
|
||||
}
|
||||
padding: 50px;
|
||||
}
|
|
@ -1,22 +1,19 @@
|
|||
import React, { useEffect, useState, useRef } from 'react';
|
||||
import JSZip from 'jszip';
|
||||
|
||||
|
||||
const Animation = ({ src = "M_Porsche_cross_arm.zip", animationWidth = "500px" }) => {
|
||||
const Animation = ({ src = "M_Porsche_cross_arm.zip", h = '100%', w = '100%' }) => {
|
||||
const [images, setImages] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const frameRate = 1000 / 24; // Original frame rate (24 FPS)
|
||||
const frameRate = 1000 / 24; // 24 FPS
|
||||
const canvasRef = useRef(null);
|
||||
const animationRef = useRef(null);
|
||||
const currentFrameRef = useRef(0);
|
||||
const imageElementsRef = useRef([]);
|
||||
const urlSource = `${import.meta.env.VITE_ASSETS_URL}/${src}`;
|
||||
|
||||
// Load images from the ZIP file
|
||||
useEffect(() => {
|
||||
const loadImages = async () => {
|
||||
const zip = new JSZip();
|
||||
// console.log(import.meta.env.VITE_ASSETS_URL);
|
||||
try {
|
||||
const response = await fetch(urlSource);
|
||||
if (!response.ok) {
|
||||
|
@ -42,13 +39,12 @@ const Animation = ({ src = "M_Porsche_cross_arm.zip", animationWidth = "500px" }
|
|||
});
|
||||
|
||||
const imgElements = await Promise.all(imgPromises);
|
||||
|
||||
if (imgElements.length === 0) {
|
||||
console.error('No images found in the ZIP file.');
|
||||
}
|
||||
|
||||
imageElementsRef.current = imgElements; // Store preloaded images
|
||||
setImages(imgElements); // Set images state
|
||||
imageElementsRef.current = imgElements;
|
||||
setImages(imgElements);
|
||||
} catch (error) {
|
||||
console.error('Error loading images:', error);
|
||||
} finally {
|
||||
|
@ -59,7 +55,6 @@ const Animation = ({ src = "M_Porsche_cross_arm.zip", animationWidth = "500px" }
|
|||
loadImages();
|
||||
}, [urlSource]);
|
||||
|
||||
// WebGL setup and animation loop
|
||||
useEffect(() => {
|
||||
if (images.length > 0) {
|
||||
const canvas = canvasRef.current;
|
||||
|
@ -69,17 +64,21 @@ const Animation = ({ src = "M_Porsche_cross_arm.zip", animationWidth = "500px" }
|
|||
return;
|
||||
}
|
||||
|
||||
// Set up WebGL context (basic)
|
||||
gl.clearColor(0.0, 0.0, 0.0, 0.0);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
const dpr = window.devicePixelRatio || 1;
|
||||
canvas.width = rect.width * dpr;
|
||||
canvas.height = rect.height * dpr;
|
||||
gl.viewport(0, 0, canvas.width, canvas.height);
|
||||
|
||||
// Enable blending
|
||||
canvas.style.width = `${rect.width}px`;
|
||||
canvas.style.height = `${rect.height}px`;
|
||||
|
||||
gl.clearColor(0.0, 0.0, 0.0, 0.0);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
|
||||
gl.enable(gl.BLEND);
|
||||
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
||||
// gl.blendFunc(gl.SRC_COLOR, gl.DST_COLOR);
|
||||
|
||||
// Shader program
|
||||
const vertexShaderSource = `
|
||||
attribute vec2 a_position;
|
||||
attribute vec2 a_texCoord;
|
||||
|
@ -103,7 +102,7 @@ const Animation = ({ src = "M_Porsche_cross_arm.zip", animationWidth = "500px" }
|
|||
gl.shaderSource(shader, source);
|
||||
gl.compileShader(shader);
|
||||
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
||||
console.error('ERROR compiling shader', gl.getShaderInfoLog(shader));
|
||||
console.error('Shader compile error:', gl.getShaderInfoLog(shader));
|
||||
}
|
||||
return shader;
|
||||
};
|
||||
|
@ -116,18 +115,17 @@ const Animation = ({ src = "M_Porsche_cross_arm.zip", animationWidth = "500px" }
|
|||
gl.attachShader(shaderProgram, fragmentShader);
|
||||
gl.linkProgram(shaderProgram);
|
||||
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
|
||||
console.error('ERROR linking program', gl.getProgramInfoLog(shaderProgram));
|
||||
console.error('Program link error:', gl.getProgramInfoLog(shaderProgram));
|
||||
}
|
||||
gl.useProgram(shaderProgram);
|
||||
|
||||
// Create buffer and set up attributes
|
||||
const positionBuffer = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
|
||||
const vertices = new Float32Array([
|
||||
-1.0, -1.0, // Bottom-left
|
||||
1.0, -1.0, // Bottom-right
|
||||
-1.0, 1.0, // Top-left
|
||||
1.0, 1.0 // Top-right
|
||||
-1.0, -1.0,
|
||||
1.0, -1.0,
|
||||
-1.0, 1.0,
|
||||
1.0, 1.0,
|
||||
]);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
|
||||
|
||||
|
@ -135,33 +133,13 @@ const Animation = ({ src = "M_Porsche_cross_arm.zip", animationWidth = "500px" }
|
|||
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
|
||||
gl.enableVertexAttribArray(positionLocation);
|
||||
|
||||
// Create texture and load image
|
||||
const texture = gl.createTexture();
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
||||
// gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
||||
// gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
||||
|
||||
const loadTexture = (image) => {
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
|
||||
|
||||
gl.generateMipmap(gl.TEXTURE_2D);
|
||||
};
|
||||
|
||||
loadTexture(imageElementsRef.current[0]); // Initially load the first image
|
||||
|
||||
// Texture coordinates
|
||||
const texCoordBuffer = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
|
||||
const texCoords = new Float32Array([
|
||||
0.0, 1.0, // Bottom-left (now corresponds to top-left of the image)
|
||||
1.0, 1.0, // Bottom-right (now corresponds to top-right of the image)
|
||||
0.0, 0.0, // Top-left (now corresponds to bottom-left of the image)
|
||||
1.0, 0.0 // Top-right (now corresponds to bottom-right of the image)
|
||||
0.0, 1.0,
|
||||
1.0, 1.0,
|
||||
0.0, 0.0,
|
||||
1.0, 0.0,
|
||||
]);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, texCoords, gl.STATIC_DRAW);
|
||||
|
||||
|
@ -169,9 +147,28 @@ const Animation = ({ src = "M_Porsche_cross_arm.zip", animationWidth = "500px" }
|
|||
gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0);
|
||||
gl.enableVertexAttribArray(texCoordLocation);
|
||||
|
||||
const texture = gl.createTexture();
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
||||
// Optionally use NEAREST for sharper image:
|
||||
// gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
||||
// gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
||||
|
||||
const uTextureLocation = gl.getUniformLocation(shaderProgram, "u_texture");
|
||||
|
||||
const loadTexture = (image) => {
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
|
||||
gl.generateMipmap(gl.TEXTURE_2D);
|
||||
};
|
||||
|
||||
loadTexture(imageElementsRef.current[0]);
|
||||
|
||||
let lastFrameTime = 0;
|
||||
|
||||
const animate = (timestamp) => {
|
||||
if (lastFrameTime === 0) lastFrameTime = timestamp;
|
||||
|
||||
|
@ -202,18 +199,12 @@ const Animation = ({ src = "M_Porsche_cross_arm.zip", animationWidth = "500px" }
|
|||
return <div>Loading...</div>;
|
||||
} else {
|
||||
return (
|
||||
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
|
||||
<canvas
|
||||
ref={canvasRef}
|
||||
width={'1920'}
|
||||
height={'1080'} // Maintain aspect ratio (example: 16:9)
|
||||
style={{ maxWidth: animationWidth, height: 'auto' }}
|
||||
style={{ height: h, width: w }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default Animation;
|
||||
|
||||
|
|
@ -2,29 +2,36 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
import '../css/global.css';
|
||||
import '../css/textBox.css';
|
||||
import '../css/nameBox.css';
|
||||
import '../css/conversation.css'
|
||||
import Animation from './components/animation.jsx';
|
||||
|
||||
function ConversationPage({data , onClicked}) {
|
||||
const backgroundSRC = `${import.meta.env.VITE_ASSETS_URL}/${data.background}`
|
||||
|
||||
return (
|
||||
<div
|
||||
onClick={onClicked}
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
height: '100vh',
|
||||
width: '100vw',
|
||||
height: '100svh',
|
||||
width: '100svw',
|
||||
flexDirection: 'column',
|
||||
backgroundImage: `url(${backgroundSRC})`,
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
}}
|
||||
>
|
||||
<div className='spriteBox'>
|
||||
<Animation src={`F${data.sprite}`}/>
|
||||
</div>
|
||||
<div style={{width:'60vw'}}>
|
||||
<div className='nameBox title'>
|
||||
{data.name}
|
||||
</div>
|
||||
<div className='textBox title' style={{marginTop: '50vh', overflow: 'scroll',}}>
|
||||
</div>
|
||||
<div className='textBox title' style={{overflow: 'scroll',}}>
|
||||
{data.text}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -4,6 +4,7 @@ import { useNavigate, useParams } from 'react-router-dom';
|
|||
import '../css/global.css';
|
||||
import { fetchYamlData } from './components/fetchYamlData';
|
||||
import StoryPage from './storyPage';
|
||||
import ConversationPage from './conversationPage.jsx';
|
||||
import LoadingScene from './components/loadingScene.jsx';
|
||||
|
||||
function VitualNovelHandler() {
|
||||
|
@ -58,7 +59,7 @@ function VitualNovelHandler() {
|
|||
case "story":
|
||||
return <StoryPage data={currentStepData} onClicked={handleNextStep}/>;
|
||||
case "conversation":
|
||||
return <div onClick={handleNextStep}>conversation</div>;
|
||||
return <ConversationPage data={currentStepData} onClicked={handleNextStep}/>;
|
||||
case "option":
|
||||
return <div onClick={handleNextStep}>option</div>;
|
||||
default:
|
||||
|
@ -67,7 +68,7 @@ function VitualNovelHandler() {
|
|||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div style={{ width: "100svw", height: "100svh" }}>
|
||||
{renderComponent(currentStepData.type)}
|
||||
</div>
|
||||
);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue