shrani
This commit is contained in:
484
docs/guides/ACCESSIBILITY_IMPLEMENTATION_PLAN.md
Normal file
484
docs/guides/ACCESSIBILITY_IMPLEMENTATION_PLAN.md
Normal file
@@ -0,0 +1,484 @@
|
||||
# ♿ ACCESSIBILITY FEATURES - IMPLEMENTATION PLAN
|
||||
|
||||
**Datum:** 12. December 2025
|
||||
**Prioriteta:** HIGH
|
||||
**Estimated Time:** 2-3 ure
|
||||
|
||||
---
|
||||
|
||||
## 🎯 **CILJI:**
|
||||
|
||||
Implementirati celovit accessibility sistem za:
|
||||
- High Contrast Modes
|
||||
- Color Blind Support
|
||||
- Photosensitivity Protection
|
||||
|
||||
---
|
||||
|
||||
## 📋 **FAZA 1: HIGH CONTRAST MODES** (30 min)
|
||||
|
||||
### **1.1 Black & White Mode**
|
||||
**Datoteka:** `src/systems/AccessibilitySystem.js` (nova)
|
||||
|
||||
```javascript
|
||||
class AccessibilitySystem {
|
||||
constructor(scene) {
|
||||
this.scene = scene;
|
||||
this.settings = {
|
||||
highContrast: 'none', // 'none', 'bw', 'yellow_black'
|
||||
largeUI: false,
|
||||
boldOutlines: false,
|
||||
colorBlindMode: 'none',
|
||||
photosensitivity: false
|
||||
};
|
||||
|
||||
this.loadSettings();
|
||||
}
|
||||
|
||||
enableBlackWhite() {
|
||||
// Apply grayscale filter to entire game
|
||||
this.scene.cameras.main.setPostPipeline('GrayscalePipeline');
|
||||
|
||||
// Increase contrast
|
||||
this.scene.cameras.main.setAlpha(1.2);
|
||||
|
||||
console.log('🎨 Black & White mode enabled');
|
||||
}
|
||||
|
||||
disableBlackWhite() {
|
||||
this.scene.cameras.main.clearPostPipeline();
|
||||
this.scene.cameras.main.setAlpha(1.0);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### **1.2 Yellow on Black Mode**
|
||||
```javascript
|
||||
enableYellowOnBlack() {
|
||||
// Create color replacement pipeline
|
||||
const pipeline = this.scene.game.renderer.pipelines.add('YellowBlackPipeline', {
|
||||
fragShader: `
|
||||
precision mediump float;
|
||||
uniform sampler2D uMainSampler;
|
||||
varying vec2 outTexCoord;
|
||||
|
||||
void main(void) {
|
||||
vec4 color = texture2D(uMainSampler, outTexCoord);
|
||||
float gray = dot(color.rgb, vec3(0.299, 0.587, 0.114));
|
||||
|
||||
// Yellow on black
|
||||
vec3 yellow = vec3(1.0, 1.0, 0.0);
|
||||
vec3 result = yellow * gray;
|
||||
|
||||
gl_FragColor = vec4(result, color.a);
|
||||
}
|
||||
`
|
||||
});
|
||||
|
||||
this.scene.cameras.main.setPostPipeline(pipeline);
|
||||
}
|
||||
```
|
||||
|
||||
### **1.3 Large UI (150%-200%)**
|
||||
```javascript
|
||||
enableLargeUI(scale = 1.5) {
|
||||
const uiScene = this.scene.scene.get('UIScene');
|
||||
if (!uiScene) return;
|
||||
|
||||
// Scale all UI elements
|
||||
uiScene.children.list.forEach(child => {
|
||||
if (child.setScale) {
|
||||
child.setScale(child.scaleX * scale, child.scaleY * scale);
|
||||
}
|
||||
});
|
||||
|
||||
// Adjust positions
|
||||
this.repositionUIElements(scale);
|
||||
|
||||
console.log(`🔍 Large UI enabled: ${scale * 100}%`);
|
||||
}
|
||||
```
|
||||
|
||||
### **1.4 Bold Outlines**
|
||||
```javascript
|
||||
enableBoldOutlines() {
|
||||
// Increase stroke thickness on all text
|
||||
const uiScene = this.scene.scene.get('UIScene');
|
||||
if (!uiScene) return;
|
||||
|
||||
uiScene.children.list.forEach(child => {
|
||||
if (child.type === 'Text') {
|
||||
child.setStroke('#000000', 6); // Thicker stroke
|
||||
child.setShadow(2, 2, '#000000', 2, true, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 **FAZA 2: COLOR BLIND SUPPORT** (45 min)
|
||||
|
||||
### **2.1 Protanopia Mode (Red-Blind)**
|
||||
```javascript
|
||||
enableProtanopia() {
|
||||
const pipeline = this.createColorBlindPipeline('protanopia', `
|
||||
// Protanopia simulation
|
||||
mat3 protanopia = mat3(
|
||||
0.567, 0.433, 0.0,
|
||||
0.558, 0.442, 0.0,
|
||||
0.0, 0.242, 0.758
|
||||
);
|
||||
|
||||
vec3 result = protanopia * color.rgb;
|
||||
`);
|
||||
|
||||
this.scene.cameras.main.setPostPipeline(pipeline);
|
||||
}
|
||||
```
|
||||
|
||||
### **2.2 Deuteranopia Mode (Green-Blind)**
|
||||
```javascript
|
||||
enableDeuteranopia() {
|
||||
const pipeline = this.createColorBlindPipeline('deuteranopia', `
|
||||
mat3 deuteranopia = mat3(
|
||||
0.625, 0.375, 0.0,
|
||||
0.7, 0.3, 0.0,
|
||||
0.0, 0.3, 0.7
|
||||
);
|
||||
|
||||
vec3 result = deuteranopia * color.rgb;
|
||||
`);
|
||||
|
||||
this.scene.cameras.main.setPostPipeline(pipeline);
|
||||
}
|
||||
```
|
||||
|
||||
### **2.3 Tritanopia Mode (Blue-Blind)**
|
||||
```javascript
|
||||
enableTritanopia() {
|
||||
const pipeline = this.createColorBlindPipeline('tritanopia', `
|
||||
mat3 tritanopia = mat3(
|
||||
0.95, 0.05, 0.0,
|
||||
0.0, 0.433, 0.567,
|
||||
0.0, 0.475, 0.525
|
||||
);
|
||||
|
||||
vec3 result = tritanopia * color.rgb;
|
||||
`);
|
||||
|
||||
this.scene.cameras.main.setPostPipeline(pipeline);
|
||||
}
|
||||
```
|
||||
|
||||
### **2.4 Achromatopsia Mode (Total Color Blind)**
|
||||
```javascript
|
||||
enableAchromatopsia() {
|
||||
// Full grayscale
|
||||
this.enableBlackWhite();
|
||||
|
||||
// Add high contrast
|
||||
this.scene.cameras.main.setContrast(1.5);
|
||||
}
|
||||
```
|
||||
|
||||
### **2.5 Shape Coding**
|
||||
```javascript
|
||||
addShapeCoding() {
|
||||
// Replace color-only indicators with shapes
|
||||
// Example: HP bar = ❤️, Hunger = 🍖, Thirst = 💧
|
||||
|
||||
const uiScene = this.scene.scene.get('UIScene');
|
||||
if (!uiScene) return;
|
||||
|
||||
// Add icons to bars
|
||||
if (uiScene.healthBar) {
|
||||
const icon = uiScene.add.text(
|
||||
uiScene.healthBar.x - 20,
|
||||
uiScene.healthBar.y,
|
||||
'❤️',
|
||||
{ fontSize: '16px' }
|
||||
);
|
||||
icon.setScrollFactor(0);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### **2.6 Pattern Overlays**
|
||||
```javascript
|
||||
addPatternOverlays() {
|
||||
// Add patterns to differentiate elements
|
||||
// Example: HP = solid, Hunger = stripes, Thirst = dots
|
||||
|
||||
const createPattern = (type) => {
|
||||
const graphics = this.scene.add.graphics();
|
||||
|
||||
if (type === 'stripes') {
|
||||
for (let i = 0; i < 10; i++) {
|
||||
graphics.lineStyle(2, 0xffffff, 0.3);
|
||||
graphics.lineBetween(i * 10, 0, i * 10, 100);
|
||||
}
|
||||
} else if (type === 'dots') {
|
||||
for (let i = 0; i < 5; i++) {
|
||||
for (let j = 0; j < 5; j++) {
|
||||
graphics.fillStyle(0xffffff, 0.3);
|
||||
graphics.fillCircle(i * 20, j * 20, 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return graphics;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 **FAZA 3: PHOTOSENSITIVITY PROTECTION** (45 min)
|
||||
|
||||
### **3.1 Flash Limiter**
|
||||
```javascript
|
||||
class FlashLimiter {
|
||||
constructor(scene) {
|
||||
this.scene = scene;
|
||||
this.flashCount = 0;
|
||||
this.flashWindow = 1000; // 1 second
|
||||
this.maxFlashes = 3; // Max 3 flashes per second
|
||||
this.lastFlashTime = 0;
|
||||
}
|
||||
|
||||
canFlash() {
|
||||
const now = Date.now();
|
||||
|
||||
// Reset counter if window passed
|
||||
if (now - this.lastFlashTime > this.flashWindow) {
|
||||
this.flashCount = 0;
|
||||
this.lastFlashTime = now;
|
||||
}
|
||||
|
||||
// Check limit
|
||||
if (this.flashCount >= this.maxFlashes) {
|
||||
console.warn('⚠️ Flash limit reached - skipping flash');
|
||||
return false;
|
||||
}
|
||||
|
||||
this.flashCount++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### **3.2 Disable Lightning Effects**
|
||||
```javascript
|
||||
disableLightning() {
|
||||
if (this.scene.weatherSystem) {
|
||||
this.scene.weatherSystem.lightningEnabled = false;
|
||||
}
|
||||
|
||||
// Remove existing lightning effects
|
||||
this.scene.children.list.forEach(child => {
|
||||
if (child.name === 'lightning') {
|
||||
child.destroy();
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### **3.3 Reduce Particles**
|
||||
```javascript
|
||||
reduceParticles(level = 0.5) {
|
||||
// Reduce particle emission rate
|
||||
this.scene.children.list.forEach(child => {
|
||||
if (child.type === 'ParticleEmitter') {
|
||||
child.setFrequency(child.frequency / level);
|
||||
child.setQuantity(Math.floor(child.quantity * level));
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### **3.4 Epilepsy Warning Screen**
|
||||
```javascript
|
||||
showEpilepsyWarning() {
|
||||
const warning = this.scene.add.container(
|
||||
this.scene.cameras.main.centerX,
|
||||
this.scene.cameras.main.centerY
|
||||
);
|
||||
warning.setDepth(10000);
|
||||
|
||||
const bg = this.scene.add.rectangle(0, 0, 600, 400, 0x000000, 0.95);
|
||||
|
||||
const title = this.scene.add.text(0, -150, '⚠️ EPILEPSY WARNING', {
|
||||
fontSize: '32px',
|
||||
color: '#ff0000',
|
||||
fontStyle: 'bold'
|
||||
}).setOrigin(0.5);
|
||||
|
||||
const text = this.scene.add.text(0, -50,
|
||||
'This game contains flashing lights and patterns\n' +
|
||||
'that may trigger seizures in people with\n' +
|
||||
'photosensitive epilepsy.\n\n' +
|
||||
'Player discretion is advised.',
|
||||
{
|
||||
fontSize: '18px',
|
||||
color: '#ffffff',
|
||||
align: 'center',
|
||||
wordWrap: { width: 500 }
|
||||
}
|
||||
).setOrigin(0.5);
|
||||
|
||||
const enableBtn = this.scene.add.text(0, 120, '[ ENABLE PHOTOSENSITIVITY MODE ]', {
|
||||
fontSize: '20px',
|
||||
color: '#00ff00',
|
||||
backgroundColor: '#003300',
|
||||
padding: { x: 20, y: 10 }
|
||||
}).setOrigin(0.5);
|
||||
|
||||
enableBtn.setInteractive({ useHandCursor: true });
|
||||
enableBtn.on('pointerdown', () => {
|
||||
this.enablePhotosensitivityMode();
|
||||
warning.destroy();
|
||||
});
|
||||
|
||||
const continueBtn = this.scene.add.text(0, 170, '[ CONTINUE WITHOUT ]', {
|
||||
fontSize: '16px',
|
||||
color: '#888888'
|
||||
}).setOrigin(0.5);
|
||||
|
||||
continueBtn.setInteractive({ useHandCursor: true });
|
||||
continueBtn.on('pointerdown', () => {
|
||||
warning.destroy();
|
||||
});
|
||||
|
||||
warning.add([bg, title, text, enableBtn, continueBtn]);
|
||||
}
|
||||
```
|
||||
|
||||
### **3.5 Motion Sickness Options**
|
||||
```javascript
|
||||
enableMotionSicknessMode() {
|
||||
// Reduce camera shake
|
||||
this.scene.cameras.main.shake = () => {};
|
||||
|
||||
// Reduce screen transitions
|
||||
this.scene.cameras.main.fadeEffect.duration = 100; // Faster fades
|
||||
|
||||
// Disable parallax
|
||||
if (this.scene.parallaxSystem) {
|
||||
this.scene.parallaxSystem.enabled = false;
|
||||
}
|
||||
|
||||
// Reduce zoom changes
|
||||
this.scene.cameras.main.zoomTo = (zoom, duration) => {
|
||||
this.scene.cameras.main.setZoom(zoom); // Instant
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### **3.6 Brightness Limiter**
|
||||
```javascript
|
||||
enableBrightnessLimiter(maxBrightness = 0.8) {
|
||||
// Limit maximum brightness
|
||||
const overlay = this.scene.add.graphics();
|
||||
overlay.fillStyle(0x000000, 1 - maxBrightness);
|
||||
overlay.fillRect(0, 0, this.scene.cameras.main.width, this.scene.cameras.main.height);
|
||||
overlay.setScrollFactor(0);
|
||||
overlay.setDepth(9999);
|
||||
|
||||
this.brightnessOverlay = overlay;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 **FAZA 4: SETTINGS MENU INTEGRATION** (30 min)
|
||||
|
||||
### **4.1 Accessibility Settings Menu**
|
||||
```javascript
|
||||
createAccessibilityMenu() {
|
||||
const menu = this.scene.add.container(
|
||||
this.scene.cameras.main.centerX,
|
||||
this.scene.cameras.main.centerY
|
||||
);
|
||||
|
||||
// Title
|
||||
const title = this.scene.add.text(0, -200, '♿ ACCESSIBILITY', {
|
||||
fontSize: '32px',
|
||||
fontStyle: 'bold'
|
||||
}).setOrigin(0.5);
|
||||
|
||||
// High Contrast
|
||||
this.addToggle(menu, 0, -150, 'High Contrast', [
|
||||
'None', 'Black & White', 'Yellow on Black'
|
||||
], (value) => this.setHighContrast(value));
|
||||
|
||||
// Large UI
|
||||
this.addToggle(menu, 0, -100, 'UI Size', [
|
||||
'100%', '150%', '200%'
|
||||
], (value) => this.setUISize(value));
|
||||
|
||||
// Color Blind Mode
|
||||
this.addToggle(menu, 0, -50, 'Color Blind Mode', [
|
||||
'None', 'Protanopia', 'Deuteranopia', 'Tritanopia', 'Achromatopsia'
|
||||
], (value) => this.setColorBlindMode(value));
|
||||
|
||||
// Photosensitivity
|
||||
this.addCheckbox(menu, 0, 0, 'Photosensitivity Protection',
|
||||
(checked) => this.setPhotosensitivity(checked));
|
||||
|
||||
// Motion Sickness
|
||||
this.addCheckbox(menu, 0, 50, 'Motion Sickness Mode',
|
||||
(checked) => this.setMotionSickness(checked));
|
||||
|
||||
menu.add(title);
|
||||
return menu;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 **IMPLEMENTATION STEPS:**
|
||||
|
||||
1. **Ustvari AccessibilitySystem.js** (30 min)
|
||||
2. **Implementiraj High Contrast Modes** (30 min)
|
||||
3. **Implementiraj Color Blind Support** (45 min)
|
||||
4. **Implementiraj Photosensitivity Protection** (45 min)
|
||||
5. **Ustvari Settings Menu** (30 min)
|
||||
6. **Integracija v GameScene** (15 min)
|
||||
7. **Testing** (30 min)
|
||||
|
||||
**Total:** 3h 45min
|
||||
|
||||
---
|
||||
|
||||
## 🔧 **DATOTEKE:**
|
||||
|
||||
**Nove:**
|
||||
- `src/systems/AccessibilitySystem.js` (~500 vrstic)
|
||||
- `src/pipelines/ColorBlindPipeline.js` (~200 vrstic)
|
||||
- `src/pipelines/GrayscalePipeline.js` (~50 vrstic)
|
||||
|
||||
**Posodobljene:**
|
||||
- `src/scenes/GameScene.js` - Initialize AccessibilitySystem
|
||||
- `src/scenes/UIScene.js` - Accessibility menu
|
||||
- `index.html` - Dodaj nove skripte
|
||||
|
||||
---
|
||||
|
||||
## 🎯 **PRIORITETA:**
|
||||
|
||||
**HIGH** - Accessibility je pomemben za:
|
||||
- Večjo dostopnost
|
||||
- Širše občinstvo
|
||||
- Boljšo uporabniško izkušnjo
|
||||
- Compliance s standardi
|
||||
|
||||
---
|
||||
|
||||
**Status:** ⏳ **PLAN PRIPRAVLJEN - ČAKA NA IMPLEMENTACIJO**
|
||||
|
||||
**Priporočam:** Implementacija v naslednji seji (jutri)
|
||||
|
||||
**Razlog:** Seja že traja 2h 14min, accessibility zahteva 3-4 ure dela.
|
||||
|
||||
Želite začeti zdaj ali pustim za jutri? 🎮
|
||||
Reference in New Issue
Block a user