KBLLR commited on
Commit
56abb95
·
verified ·
1 Parent(s): e300bff

the animation loop is not working properly

Browse files
Files changed (1) hide show
  1. index.html +114 -304
index.html CHANGED
@@ -12,177 +12,72 @@
12
  body {
13
  margin: 0;
14
  overflow: hidden;
15
- font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
16
- background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%);
17
  }
18
  #canvas {
19
  display: block;
20
  }
21
  .transition-all {
22
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
23
- }
24
- .character-card {
25
- background: linear-gradient(145deg, rgba(30, 41, 59, 0.9), rgba(15, 23, 42, 0.9));
26
- backdrop-filter: blur(10px);
27
- border: 1px solid rgba(255, 255, 255, 0.1);
28
- box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
29
  }
30
  .character-card:hover {
31
- transform: translateY(-8px) scale(1.02);
32
- box-shadow: 0 20px 40px rgba(0, 0, 0, 0.4);
33
- border-color: rgba(139, 92, 246, 0.5);
34
  }
35
  .dialog-box {
36
- background: rgba(15, 23, 42, 0.95);
37
- backdrop-filter: blur(20px);
38
- border: 1px solid rgba(255, 255, 255, 0.1);
39
- box-shadow: 0 25px 50px rgba(0, 0, 0, 0.5);
40
  }
41
  .character-preview {
42
  width: 100%;
43
  height: 200px;
44
- background: linear-gradient(145deg, rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.02));
45
- border-radius: 1rem;
46
- border: 1px solid rgba(255, 255, 255, 0.1);
47
- position: relative;
48
- overflow: hidden;
49
- }
50
- .character-preview::before {
51
- content: '';
52
- position: absolute;
53
- top: 0;
54
- left: 0;
55
- right: 0;
56
- bottom: 0;
57
- background: linear-gradient(45deg, transparent, rgba(255, 255, 255, 0.05), transparent);
58
- opacity: 0;
59
- transition: opacity 0.3s ease;
60
- }
61
- .character-card:hover .character-preview::before {
62
- opacity: 1;
63
- }
64
- .glass-effect {
65
  background: rgba(255, 255, 255, 0.1);
66
- backdrop-filter: blur(10px);
67
- border: 1px solid rgba(255, 255, 255, 0.2);
68
- }
69
- .gradient-text {
70
- background: linear-gradient(135deg, #8b5cf6 0%, #3b82f6 100%);
71
- -webkit-background-clip: text;
72
- -webkit-text-fill-color: transparent;
73
- background-clip: text;
74
- }
75
- .floating-animation {
76
- animation: float 6s ease-in-out infinite;
77
- }
78
- @keyframes float {
79
- 0%, 100% { transform: translateY(0px); }
80
- 50% { transform: translateY(-10px); }
81
- }
82
- .pulse-glow {
83
- animation: pulse-glow 2s ease-in-out infinite alternate;
84
- }
85
- @keyframes pulse-glow {
86
- from { box-shadow: 0 0 20px rgba(139, 92, 246, 0.4); }
87
- to { box-shadow: 0 0 30px rgba(139, 92, 246, 0.8); }
88
  }
89
  </style>
90
  </head>
91
  <body class="bg-gray-900 text-white">
92
  <!-- Welcome Screen -->
93
- <div id="welcome-screen" class="fixed inset-0 flex items-center justify-center bg-gradient-to-br from-slate-900 via-purple-900 to-slate-900 z-50 transition-all duration-500">
94
- <div class="absolute inset-0 overflow-hidden">
95
- <div class="absolute -top-1/2 -left-1/2 w-full h-full bg-gradient-conic from-transparent via-purple-500/10 to-transparent animate-spin-slow"></div>
96
- <div class="absolute -top-1/2 -right-1/2 w-full h-full bg-gradient-conic from-transparent via-blue-500/10 to-transparent animate-spin-slow" style="animation-delay: -3s;"></div>
97
- </div>
98
- <div class="text-center max-w-4xl p-12 glass-effect rounded-3xl shadow-2xl relative z-10 border border-white/10">
99
- <div class="floating-animation mb-8">
100
- <div class="w-32 h-32 mx-auto bg-gradient-to-r from-purple-500 to-blue-500 rounded-full flex items-center justify-center shadow-2xl">
101
- <svg class="w-16 h-16 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
102
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
103
- </svg>
104
- </div>
105
- </div>
106
- <h1 class="text-6xl font-black mb-6 gradient-text tracking-tight">CharacterVerse</h1>
107
- <p class="text-2xl mb-10 text-gray-200 leading-relaxed">Immerse yourself in an expansive 3D universe where every character has a story. Choose your avatar and embark on an unforgettable journey through dynamic worlds filled with interactive companions.</p>
108
- <button id="start-btn" class="px-12 py-4 bg-gradient-to-r from-purple-600 to-blue-600 rounded-2xl text-white font-bold text-xl hover:from-purple-700 hover:to-blue-700 transition-all transform hover:scale-110 shadow-2xl pulse-glow border border-white/20">
109
- <span class="flex items-center justify-center space-x-3">
110
- <span>Begin Epic Journey</span>
111
- <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
112
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7l5 5m0 0l-5 5m5-5H6"></path>
113
- </svg>
114
- </span>
115
  </button>
116
- <div class="mt-8 flex justify-center space-x-6 text-sm text-gray-400">
117
- <div class="flex items-center space-x-2">
118
- <div class="w-2 h-2 bg-green-500 rounded-full"></div>
119
- <span>Real-time 3D Graphics</span>
120
- </div>
121
- <div class="flex items-center space-x-2">
122
- <div class="w-2 h-2 bg-blue-500 rounded-full"></div>
123
- <span>Interactive Characters</span>
124
- </div>
125
- <div class="flex items-center space-x-2">
126
- <div class="w-2 h-2 bg-purple-500 rounded-full"></div>
127
- <span>Dynamic Environments</span>
128
- </div>
129
- </div>
130
  </div>
131
  </div>
132
  <!-- Character Selection Screen -->
133
- <div id="character-selection" class="fixed inset-0 bg-gradient-to-br from-slate-900 via-purple-900 to-slate-900 z-40 transition-all duration-500 opacity-0 pointer-events-none">
134
- <div class="absolute inset-0 bg-black/20 backdrop-blur-sm"></div>
135
- <div class="container mx-auto px-6 py-8 h-full flex flex-col relative z-10">
136
- <div class="text-center mb-12">
137
- <h2 class="text-5xl font-black mb-4 gradient-text">Choose Your Champion</h2>
138
- <p class="text-xl text-gray-300 max-w-2xl mx-auto">Select the perfect avatar that represents your journey. Each character comes with unique abilities and personality traits.</p>
139
- </div>
140
- <div class="flex-1 overflow-y-auto pb-32">
141
- <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-8 max-w-7xl mx-auto">
142
  <!-- Character cards will be dynamically inserted here -->
143
  </div>
144
  </div>
145
- <div class="fixed bottom-0 left-0 right-0 bg-gradient-to-t from-slate-900/95 via-slate-900/80 to-transparent py-8 px-6 z-50 backdrop-blur-lg border-t border-white/10">
146
  <div class="container mx-auto text-center">
147
- <button id="confirm-character" class="px-12 py-4 bg-gradient-to-r from-green-600 to-emerald-500 rounded-2xl text-white font-bold text-lg hover:from-green-700 hover:to-emerald-600 transition-all transform hover:scale-105 shadow-2xl border border-white/20 opacity-0 pulse-glow">
148
- <span class="flex items-center justify-center space-x-3">
149
- <span>Confirm Champion</span>
150
- <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
151
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
152
- </svg>
153
- </span>
154
  </button>
155
  </div>
156
  </div>
157
  </div>
158
  </div>
159
  <!-- World Selection Screen -->
160
- <div id="world-selection" class="fixed inset-0 bg-gradient-to-br from-slate-900 via-blue-900 to-slate-900 z-30 transition-all duration-500 opacity-0 pointer-events-none">
161
- <div class="absolute inset-0 bg-black/20 backdrop-blur-sm"></div>
162
- <div class="container mx-auto px-6 py-8 h-full flex flex-col relative z-10">
163
- <div class="text-center mb-12">
164
- <h2 class="text-5xl font-black mb-4 gradient-text">Forge Your Universe</h2>
165
- <p class="text-xl text-gray-300 max-w-2xl mx-auto">Select 5 unique characters to populate your world. Each will bring their own stories and interactions to your adventure.</p>
166
- <div class="mt-4 inline-flex items-center space-x-4 bg-white/5 rounded-full px-6 py-3 border border-white/10">
167
- <span class="text-green-400 font-semibold" id="selected-count">0</span>
168
- <span class="text-gray-400">/</span>
169
- <span class="text-gray-300">5 Characters Selected</span>
170
- </div>
171
- </div>
172
- <div class="flex-1 overflow-y-auto pb-32">
173
- <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-5 gap-8 max-w-7xl mx-auto">
174
  <!-- World character cards will be dynamically inserted here -->
175
  </div>
176
  </div>
177
- <div class="fixed bottom-0 left-0 right-0 bg-gradient-to-t from-slate-900/95 via-slate-900/80 to-transparent py-8 px-6 z-50 backdrop-blur-lg border-t border-white/10">
178
  <div class="container mx-auto text-center">
179
- <button id="start-world" class="px-12 py-4 bg-gradient-to-r from-purple-600 to-blue-600 rounded-2xl text-white font-bold text-lg hover:from-purple-700 hover:to-blue-700 transition-all transform hover:scale-105 shadow-2xl border border-white/20 opacity-0 pulse-glow">
180
- <span class="flex items-center justify-center space-x-3">
181
- <span>Generate Dynamic World</span>
182
- <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
183
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"></path>
184
- </svg>
185
- </span>
186
  </button>
187
  </div>
188
  </div>
@@ -395,6 +290,7 @@
395
  container.appendChild(card);
396
  });
397
  }
 
398
  function initThreeJS() {
399
  // Set up Three.js scene
400
  scene = new THREE.Scene();
@@ -405,15 +301,9 @@
405
  camera.position.set(0, 5, 10);
406
 
407
  // Renderer
408
- const canvas = document.getElementById('canvas');
409
- renderer = new THREE.WebGLRenderer({
410
- canvas: canvas,
411
- antialias: true,
412
- alpha: true
413
- });
414
  renderer.setSize(window.innerWidth, window.innerHeight);
415
  renderer.shadowMap.enabled = true;
416
- renderer.shadowMap.type = THREE.PCFSoftShadowMap;
417
 
418
  // Clock for animations
419
  clock = new THREE.Clock();
@@ -422,20 +312,14 @@
422
  loader = new THREE.GLTFLoader();
423
 
424
  // Add lights
425
- const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
426
  scene.add(ambientLight);
427
 
428
  const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
429
- directionalLight.position.set(10, 20, 15);
430
  directionalLight.castShadow = true;
431
  directionalLight.shadow.mapSize.width = 2048;
432
  directionalLight.shadow.mapSize.height = 2048;
433
- directionalLight.shadow.camera.near = 0.5;
434
- directionalLight.shadow.camera.far = 50;
435
- directionalLight.shadow.camera.left = -20;
436
- directionalLight.shadow.camera.right = 20;
437
- directionalLight.shadow.camera.top = 20;
438
- directionalLight.shadow.camera.bottom = -20;
439
  scene.add(directionalLight);
440
 
441
  // Create ground
@@ -469,7 +353,8 @@
469
  renderer.setSize(window.innerWidth, window.innerHeight);
470
  });
471
  }
472
- function addEnvironmentObjects() {
 
473
  // Add some trees
474
  const treeGeometry = new THREE.ConeGeometry(1, 3, 8);
475
  const treeMaterial = new THREE.MeshStandardMaterial({ color: 0x2e7d32 });
@@ -558,11 +443,12 @@ function addEnvironmentObjects() {
558
 
559
  group.position.y = 0;
560
  scene.add(group);
 
561
  gameState.player = {
562
  model: group,
563
- speed: 0.08,
564
- runSpeed: 0.15,
565
- rotationSpeed: 0.08,
566
  isMoving: false,
567
  animations: {
568
  idle: null,
@@ -572,7 +458,8 @@ function addEnvironmentObjects() {
572
  },
573
  currentAnimation: null
574
  };
575
- // Add a simple animation mixer for the player
 
576
  mixer = new THREE.AnimationMixer(group);
577
 
578
  // Create simple animations
@@ -581,39 +468,44 @@ function addEnvironmentObjects() {
581
  // Set initial animation
582
  setPlayerAnimation('idle');
583
  }
 
584
  function createPlayerAnimations() {
585
  // In a real app, these would come from the GLTF model
586
- // For this example, we'll create simple animations using NumberKeyframeTrack
587
 
588
  // Idle animation (slight bounce)
589
- const idleTrack = new THREE.NumberKeyframeTrack(
590
- '.position[y]',
591
  [0, 0.5, 1],
592
- [0, 0.1, 0]
 
 
 
 
593
  );
594
  const idleClip = new THREE.AnimationClip('idle', 1, [idleTrack]);
595
  gameState.player.animations.idle = idleClip;
596
 
597
  // Walk animation (arm and leg movement)
598
- const leftArmTrack = new THREE.NumberKeyframeTrack(
599
  '.children[2].rotation[z]',
600
  [0, 0.5, 1],
601
- [0.3, -0.3, 0.3]
602
  );
603
- const rightArmTrack = new THREE.NumberKeyframeTrack(
604
  '.children[3].rotation[z]',
605
  [0, 0.5, 1],
606
- [-0.3, 0.3, -0.3]
607
  );
608
- const leftLegTrack = new THREE.NumberKeyframeTrack(
609
  '.children[4].position[y]',
610
  [0, 0.5, 1],
611
- [-0.4, -0.3, -0.4]
612
  );
613
- const rightLegTrack = new THREE.NumberKeyframeTrack(
614
  '.children[5].position[y]',
615
  [0, 0.5, 1],
616
- [-0.3, -0.4, -0.3]
617
  );
618
  const walkClip = new THREE.AnimationClip('walk', 0.5, [
619
  leftArmTrack, rightArmTrack, leftLegTrack, rightLegTrack
@@ -627,14 +519,15 @@ function addEnvironmentObjects() {
627
  gameState.player.animations.run = runClip;
628
 
629
  // Jump animation
630
- const jumpTrack = new THREE.NumberKeyframeTrack(
631
  '.position[y]',
632
  [0, 0.2, 0.4, 0.6, 0.8, 1],
633
- [0, 1.5, 1, 0.3, 0, 0]
634
  );
635
  const jumpClip = new THREE.AnimationClip('jump', 1, [jumpTrack]);
636
  gameState.player.animations.jump = jumpClip;
637
  }
 
638
  function setPlayerAnimation(name) {
639
  if (gameState.player.currentAnimation === name) return;
640
 
@@ -645,14 +538,14 @@ function addEnvironmentObjects() {
645
  if (clip) {
646
  const action = mixer.clipAction(clip);
647
  action.setLoop(THREE.LoopRepeat);
648
- action.clampWhenFinished = false;
649
  action.play();
650
  }
651
 
652
  gameState.player.currentAnimation = name;
653
  }
654
  }
655
- function loadNPCCharacters() {
 
656
  gameState.selectedWorldCharacters.forEach((character, index) => {
657
  // In a real app, we would load the GLTF model from the URL
658
  // For this example, we'll create a simple placeholder
@@ -701,9 +594,12 @@ function loadNPCCharacters() {
701
  });
702
  }
703
  function animate() {
704
- requestAnimationFrame(animate);
 
 
 
705
 
706
- const delta = Math.min(clock.getDelta(), 0.1); // Cap delta to prevent large jumps
707
 
708
  // Update player animation mixer
709
  if (mixer) {
@@ -717,105 +613,59 @@ function loadNPCCharacters() {
717
  checkForNearbyNPCs();
718
 
719
  renderer.render(scene, camera);
 
 
720
  }
721
- function handlePlayerMovement(delta) {
722
  if (!gameState.player) return;
723
 
724
  const player = gameState.player;
725
  let moving = false;
726
- let moveDirection = new THREE.Vector3();
727
-
728
- // Check if dialog is open - disable movement if dialog is active
729
- const isDialogOpen = !document.getElementById('dialog-box').classList.contains('translate-y-full');
730
- if (isDialogOpen) {
731
- // Stop any movement and reset animation
732
- if (player.isMoving) {
733
- player.isMoving = false;
734
- updatePlayerAnimation();
735
- }
736
- return;
737
- }
738
 
739
  // Forward/backward movement
740
  if (gameState.keys.w) {
741
- moveDirection.z = -1;
742
  moving = true;
743
  }
744
  if (gameState.keys.s) {
745
- moveDirection.z = 1;
746
  moving = true;
747
  }
748
 
749
  // Left/right movement
750
  if (gameState.keys.a) {
751
- moveDirection.x = -1;
752
  moving = true;
753
  }
754
  if (gameState.keys.d) {
755
- moveDirection.x = 1;
756
  moving = true;
757
  }
758
 
759
- // Normalize direction vector if moving diagonally
760
  if (moving) {
761
- moveDirection.normalize();
762
-
763
- // Calculate speed with delta time for consistent movement
764
- const speed = (gameState.keys.shift ? player.runSpeed : player.speed) * delta * 60;
765
-
766
- // Apply movement relative to camera direction
767
- const cameraForward = new THREE.Vector3();
768
- camera.getWorldDirection(cameraForward);
769
- cameraForward.y = 0;
770
- cameraForward.normalize();
771
-
772
- const cameraRight = new THREE.Vector3();
773
- cameraRight.crossVectors(cameraForward, new THREE.Vector3(0, 1, 0));
774
- cameraRight.normalize();
775
 
776
- const moveVector = new THREE.Vector3();
777
- moveVector.addScaledVector(cameraForward, moveDirection.z * speed);
778
- moveVector.addScaledVector(cameraRight, moveDirection.x * speed);
779
-
780
- player.model.position.add(moveVector);
781
-
782
- // Update player rotation to face movement direction
783
- if (moveDirection.length() > 0.1) {
784
- const targetRotation = Math.atan2(moveDirection.x, moveDirection.z) + camera.rotation.y;
785
- player.model.rotation.y = THREE.MathUtils.lerp(player.model.rotation.y, targetRotation, player.rotationSpeed * delta * 60);
786
- }
787
  }
788
 
789
  // Jumping - only trigger when not showing dialog and not already jumping
790
- if (gameState.keys.space && !gameState.isJumping && !isDialogOpen) {
791
  gameState.isJumping = true;
792
  setPlayerAnimation('jump');
793
-
794
- // Create a more robust jump animation
795
- let jumpStartTime = performance.now();
796
- const jumpDuration = 1000; // 1 second
797
- const originalY = player.model.position.y;
798
- const jumpHeight = 1.5;
799
-
800
- function performJump() {
801
- const currentTime = performance.now();
802
- const elapsed = currentTime - jumpStartTime;
803
- const progress = Math.min(elapsed / jumpDuration, 1);
804
-
805
- // Parabolic jump curve
806
- const jumpProgress = 1 - Math.pow(2 * progress - 1, 2);
807
- player.model.position.y = originalY + jumpHeight * jumpProgress;
808
-
809
- if (progress < 1) {
810
- requestAnimationFrame(performJump);
811
- } else {
812
- gameState.isJumping = false;
813
- player.model.position.y = originalY;
814
- updatePlayerAnimation();
815
- }
816
- }
817
-
818
- performJump();
819
  }
820
 
821
  // Update animation based on movement
@@ -824,9 +674,10 @@ function handlePlayerMovement(delta) {
824
  updatePlayerAnimation();
825
  }
826
 
827
- // Update camera position to follow player with smooth interpolation
828
- const targetCameraPosition = player.model.position.clone().add(new THREE.Vector3(0, 5, 10));
829
- camera.position.lerp(targetCameraPosition, 0.1 * delta * 60);
 
830
  camera.lookAt(player.model.position);
831
  }
832
  function updatePlayerAnimation() {
@@ -906,73 +757,32 @@ function showDialog(npc) {
906
  existingPrompt.remove();
907
  }
908
  }
909
- function handleKeyDown(event) {
910
- // Prevent default for space to avoid page scrolling
911
- if (event.key === ' ') {
912
- event.preventDefault();
913
- }
914
-
915
- // Ignore key repeats
916
- if (event.repeat) return;
917
-
918
- const key = event.key.toLowerCase();
919
- switch (key) {
920
- case 'w':
921
- gameState.keys.w = true;
922
- break;
923
- case 'a':
924
- gameState.keys.a = true;
925
- break;
926
- case 's':
927
- gameState.keys.s = true;
928
- break;
929
- case 'd':
930
- gameState.keys.d = true;
931
- break;
932
- case 'shift':
933
- gameState.keys.shift = true;
934
- break;
935
  case ' ':
936
  gameState.keys.space = true;
937
- // Check if we're near an NPC and dialog isn't already open
938
- const isDialogOpen = !document.getElementById('dialog-box').classList.contains('translate-y-full');
939
- if (gameState.nearbyNpc && !gameState.isJumping && !isDialogOpen) {
940
  showDialog(gameState.nearbyNpc);
 
941
  }
942
  break;
943
  }
944
  }
945
- function handleKeyUp(event) {
946
- // Ignore key repeats
947
- if (event.repeat) return;
948
-
949
- const key = event.key.toLowerCase();
950
- switch (key) {
951
- case 'w':
952
- gameState.keys.w = false;
953
- break;
954
- case 'a':
955
- gameState.keys.a = false;
956
- break;
957
- case 's':
958
- gameState.keys.s = false;
959
- break;
960
- case 'd':
961
- gameState.keys.d = false;
962
- break;
963
- case 'shift':
964
- gameState.keys.shift = false;
965
- break;
966
- case ' ':
967
- gameState.keys.space = false;
968
- break;
969
- }
970
-
971
- // Update animation when keys are released
972
- if (gameState.player) {
973
- updatePlayerAnimation();
974
  }
975
  }
976
- </script>
977
  <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=KBLLR/character-selector" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
978
  </html>
 
12
  body {
13
  margin: 0;
14
  overflow: hidden;
15
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
 
16
  }
17
  #canvas {
18
  display: block;
19
  }
20
  .transition-all {
21
+ transition: all 0.3s ease;
 
 
 
 
 
 
22
  }
23
  .character-card:hover {
24
+ transform: translateY(-5px);
25
+ box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);
 
26
  }
27
  .dialog-box {
28
+ background: rgba(0, 0, 0, 0.8);
29
+ backdrop-filter: blur(5px);
 
 
30
  }
31
  .character-preview {
32
  width: 100%;
33
  height: 200px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  background: rgba(255, 255, 255, 0.1);
35
+ border-radius: 0.5rem;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  }
37
  </style>
38
  </head>
39
  <body class="bg-gray-900 text-white">
40
  <!-- Welcome Screen -->
41
+ <div id="welcome-screen" class="fixed inset-0 flex items-center justify-center bg-gray-900 z-50 transition-all duration-500">
42
+ <div class="text-center max-w-2xl p-8 bg-gray-800 rounded-xl shadow-2xl">
43
+ <h1 class="text-5xl font-bold mb-6 bg-gradient-to-r from-purple-500 to-blue-500 bg-clip-text text-transparent">3D Character World</h1>
44
+ <p class="text-xl mb-8 text-gray-300">Explore a vibrant 3D world with interactive characters. Select your avatar and meet others along your journey!</p>
45
+ <button id="start-btn" class="px-8 py-3 bg-gradient-to-r from-purple-600 to-blue-600 rounded-full text-white font-bold text-lg hover:from-purple-700 hover:to-blue-700 transition-all transform hover:scale-105 shadow-lg">
46
+ Begin Adventure
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  </button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  </div>
49
  </div>
50
  <!-- Character Selection Screen -->
51
+ <div id="character-selection" class="fixed inset-0 bg-gray-900 z-40 transition-all duration-500 opacity-0 pointer-events-none">
52
+ <div class="container mx-auto px-4 py-8 h-full flex flex-col">
53
+ <h2 class="text-3xl font-bold mb-8 text-center">Choose Your Character</h2>
54
+ <div class="flex-1 overflow-y-auto pb-24">
55
+ <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
 
 
 
 
56
  <!-- Character cards will be dynamically inserted here -->
57
  </div>
58
  </div>
59
+ <div class="fixed bottom-0 left-0 right-0 bg-gray-900 border-t border-gray-700 py-4 px-4 z-50">
60
  <div class="container mx-auto text-center">
61
+ <button id="confirm-character" class="px-8 py-3 bg-green-600 rounded-full text-white font-bold text-lg hover:bg-green-700 transition-all transform hover:scale-105 shadow-lg opacity-0">
62
+ Confirm Selection
 
 
 
 
 
63
  </button>
64
  </div>
65
  </div>
66
  </div>
67
  </div>
68
  <!-- World Selection Screen -->
69
+ <div id="world-selection" class="fixed inset-0 bg-gray-900 z-30 transition-all duration-500 opacity-0 pointer-events-none">
70
+ <div class="container mx-auto px-4 py-8 h-full flex flex-col">
71
+ <h2 class="text-3xl font-bold mb-8 text-center">Select 5 Characters for Your World</h2>
72
+ <div class="flex-1 overflow-y-auto pb-24">
73
+ <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-5 gap-6">
 
 
 
 
 
 
 
 
 
74
  <!-- World character cards will be dynamically inserted here -->
75
  </div>
76
  </div>
77
+ <div class="fixed bottom-0 left-0 right-0 bg-gray-900 border-t border-gray-700 py-4 px-4 z-50">
78
  <div class="container mx-auto text-center">
79
+ <button id="start-world" class="px-8 py-3 bg-green-600 rounded-full text-white font-bold text-lg hover:bg-green-700 transition-all transform hover:scale-105 shadow-lg opacity-0">
80
+ Generate World
 
 
 
 
 
81
  </button>
82
  </div>
83
  </div>
 
290
  container.appendChild(card);
291
  });
292
  }
293
+
294
  function initThreeJS() {
295
  // Set up Three.js scene
296
  scene = new THREE.Scene();
 
301
  camera.position.set(0, 5, 10);
302
 
303
  // Renderer
304
+ renderer = new THREE.WebGLRenderer({ canvas: document.getElementById('canvas'), antialias: true });
 
 
 
 
 
305
  renderer.setSize(window.innerWidth, window.innerHeight);
306
  renderer.shadowMap.enabled = true;
 
307
 
308
  // Clock for animations
309
  clock = new THREE.Clock();
 
312
  loader = new THREE.GLTFLoader();
313
 
314
  // Add lights
315
+ const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
316
  scene.add(ambientLight);
317
 
318
  const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
319
+ directionalLight.position.set(5, 10, 7);
320
  directionalLight.castShadow = true;
321
  directionalLight.shadow.mapSize.width = 2048;
322
  directionalLight.shadow.mapSize.height = 2048;
 
 
 
 
 
 
323
  scene.add(directionalLight);
324
 
325
  // Create ground
 
353
  renderer.setSize(window.innerWidth, window.innerHeight);
354
  });
355
  }
356
+
357
+ function addEnvironmentObjects() {
358
  // Add some trees
359
  const treeGeometry = new THREE.ConeGeometry(1, 3, 8);
360
  const treeMaterial = new THREE.MeshStandardMaterial({ color: 0x2e7d32 });
 
443
 
444
  group.position.y = 0;
445
  scene.add(group);
446
+
447
  gameState.player = {
448
  model: group,
449
+ speed: 0.1,
450
+ runSpeed: 0.2,
451
+ rotationSpeed: 0.05,
452
  isMoving: false,
453
  animations: {
454
  idle: null,
 
458
  },
459
  currentAnimation: null
460
  };
461
+
462
+ // Add a simple animation mixer for the player
463
  mixer = new THREE.AnimationMixer(group);
464
 
465
  // Create simple animations
 
468
  // Set initial animation
469
  setPlayerAnimation('idle');
470
  }
471
+
472
  function createPlayerAnimations() {
473
  // In a real app, these would come from the GLTF model
474
+ // For this example, we'll create simple animations
475
 
476
  // Idle animation (slight bounce)
477
+ const idleTrack = new THREE.VectorKeyframeTrack(
478
+ '.position',
479
  [0, 0.5, 1],
480
+ [
481
+ 0, 0, 0, // Start position
482
+ 0, 0.05, 0, // Up position
483
+ 0, 0, 0 // Back to start
484
+ ]
485
  );
486
  const idleClip = new THREE.AnimationClip('idle', 1, [idleTrack]);
487
  gameState.player.animations.idle = idleClip;
488
 
489
  // Walk animation (arm and leg movement)
490
+ const leftArmTrack = new THREE.VectorKeyframeTrack(
491
  '.children[2].rotation[z]',
492
  [0, 0.5, 1],
493
+ [0.5, -0.5, 0.5]
494
  );
495
+ const rightArmTrack = new THREE.VectorKeyframeTrack(
496
  '.children[3].rotation[z]',
497
  [0, 0.5, 1],
498
+ [-0.5, 0.5, -0.5]
499
  );
500
+ const leftLegTrack = new THREE.VectorKeyframeTrack(
501
  '.children[4].position[y]',
502
  [0, 0.5, 1],
503
+ [-0.4, -0.2, -0.4]
504
  );
505
+ const rightLegTrack = new THREE.VectorKeyframeTrack(
506
  '.children[5].position[y]',
507
  [0, 0.5, 1],
508
+ [-0.2, -0.4, -0.2]
509
  );
510
  const walkClip = new THREE.AnimationClip('walk', 0.5, [
511
  leftArmTrack, rightArmTrack, leftLegTrack, rightLegTrack
 
519
  gameState.player.animations.run = runClip;
520
 
521
  // Jump animation
522
+ const jumpTrack = new THREE.VectorKeyframeTrack(
523
  '.position[y]',
524
  [0, 0.2, 0.4, 0.6, 0.8, 1],
525
+ [0, 2, 1.5, 0.5, 0, 0]
526
  );
527
  const jumpClip = new THREE.AnimationClip('jump', 1, [jumpTrack]);
528
  gameState.player.animations.jump = jumpClip;
529
  }
530
+
531
  function setPlayerAnimation(name) {
532
  if (gameState.player.currentAnimation === name) return;
533
 
 
538
  if (clip) {
539
  const action = mixer.clipAction(clip);
540
  action.setLoop(THREE.LoopRepeat);
 
541
  action.play();
542
  }
543
 
544
  gameState.player.currentAnimation = name;
545
  }
546
  }
547
+
548
+ function loadNPCCharacters() {
549
  gameState.selectedWorldCharacters.forEach((character, index) => {
550
  // In a real app, we would load the GLTF model from the URL
551
  // For this example, we'll create a simple placeholder
 
594
  });
595
  }
596
  function animate() {
597
+ if (!gameState.player) {
598
+ requestAnimationFrame(animate);
599
+ return;
600
+ }
601
 
602
+ const delta = clock.getDelta();
603
 
604
  // Update player animation mixer
605
  if (mixer) {
 
613
  checkForNearbyNPCs();
614
 
615
  renderer.render(scene, camera);
616
+
617
+ requestAnimationFrame(animate);
618
  }
619
+ function handlePlayerMovement(delta) {
620
  if (!gameState.player) return;
621
 
622
  const player = gameState.player;
623
  let moving = false;
 
 
 
 
 
 
 
 
 
 
 
 
624
 
625
  // Forward/backward movement
626
  if (gameState.keys.w) {
627
+ player.model.translateZ(-player.speed * (gameState.keys.shift ? player.runSpeed / player.speed : 1));
628
  moving = true;
629
  }
630
  if (gameState.keys.s) {
631
+ player.model.translateZ(player.speed);
632
  moving = true;
633
  }
634
 
635
  // Left/right movement
636
  if (gameState.keys.a) {
637
+ player.model.translateX(-player.speed);
638
  moving = true;
639
  }
640
  if (gameState.keys.d) {
641
+ player.model.translateX(player.speed);
642
  moving = true;
643
  }
644
 
645
+ // Rotation
646
  if (moving) {
647
+ // Calculate target rotation based on movement direction
648
+ const targetRotation = Math.atan2(
649
+ (gameState.keys.a ? -1 : 0) + (gameState.keys.d ? 1 : 0),
650
+ (gameState.keys.w ? -1 : 0) + (gameState.keys.s ? 1 : 0)
651
+ );
 
 
 
 
 
 
 
 
 
652
 
653
+ // Smoothly rotate towards target
654
+ player.model.rotation.y = THREE.MathUtils.lerp(
655
+ player.model.rotation.y,
656
+ targetRotation,
657
+ player.rotationSpeed
658
+ );
 
 
 
 
 
659
  }
660
 
661
  // Jumping - only trigger when not showing dialog and not already jumping
662
+ if (gameState.keys.space && !gameState.isJumping && !document.getElementById('dialog-box').classList.contains('translate-y-full')) {
663
  gameState.isJumping = true;
664
  setPlayerAnimation('jump');
665
+ setTimeout(() => {
666
+ gameState.isJumping = false;
667
+ updatePlayerAnimation();
668
+ }, 1000);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
669
  }
670
 
671
  // Update animation based on movement
 
674
  updatePlayerAnimation();
675
  }
676
 
677
+ // Update camera position to follow player
678
+ const cameraOffset = new THREE.Vector3(0, 5, 10);
679
+ cameraOffset.applyQuaternion(player.model.quaternion);
680
+ camera.position.copy(player.model.position.clone().add(cameraOffset));
681
  camera.lookAt(player.model.position);
682
  }
683
  function updatePlayerAnimation() {
 
757
  existingPrompt.remove();
758
  }
759
  }
760
+ function handleKeyDown(event) {
761
+ switch (event.key.toLowerCase()) {
762
+ case 'w': gameState.keys.w = true; break;
763
+ case 'a': gameState.keys.a = true; break;
764
+ case 's': gameState.keys.s = true; break;
765
+ case 'd': gameState.keys.d = true; break;
766
+ case 'shift': gameState.keys.shift = true; break;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
767
  case ' ':
768
  gameState.keys.space = true;
769
+ if (gameState.nearbyNpc && !gameState.isJumping) {
 
 
770
  showDialog(gameState.nearbyNpc);
771
+ event.preventDefault(); // Prevent default space behavior
772
  }
773
  break;
774
  }
775
  }
776
+ function handleKeyUp(event) {
777
+ switch (event.key.toLowerCase()) {
778
+ case 'w': gameState.keys.w = false; break;
779
+ case 'a': gameState.keys.a = false; break;
780
+ case 's': gameState.keys.s = false; break;
781
+ case 'd': gameState.keys.d = false; break;
782
+ case 'shift': gameState.keys.shift = false; break;
783
+ case ' ': gameState.keys.space = false; break;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
784
  }
785
  }
786
+ </script>
787
  <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=KBLLR/character-selector" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
788
  </html>