Useiden liikkuvien hahmojen hallinta js -pelissä.

Liittynyt
11.10.2021
Viestejä
38
Olen kysellyt vinkkejä toisella foorumilla, saamatta vastausta.

Kehittämässäni pelissä ns. vihollis hahmot ovat nyt tallennettu hahmoista huolehtivaan listaan objekteihin, jotka sisältävät sijaintitiedot, sekä hahmo laji kohtaiset ominaisuudet. Uudelle hahmolle arvotaan ensin laji (väri), jonka perusteella uudelle objektille haetaan ennalta määritellyt lajikohtaiset ominaisuudet toisesta objektista (listalta johon on tallennettu ennalta määritellyt ominaisuudet). Tämän lisäksi uudelle hahmolle arvotaan nopeus ja sijainti.

Nykyinen ratkaisu riittänee hyvin projektiini. Ratkaisu ei liene kovinkaan hyvä ja haluaisin luoda paremman tavan hahmojen hallintaan ja ylläpitoon. Hahmot sisältävät paljon samaa tietoa, mutta niitä tulee käsitellä hieman eritavoin. Hahmoista (keltainen, sininen) saa erimäärän pisteitä ja joitakin (punaisia) hahmoja tulee varoa.

verkkopeli

Pelin koodit:
JavaScript:
const gameVersion = "v0.6.0"

var movementInterval; // Muuttuja liikkeen päivitystä varten

let gameDifficulty = 0;

// Määritä enimmäiskalatilanne peliruudulla
// NOTICE const alkuliite muutettu, jotta pelin vaikeutumisen yhteydessä ruudussa olevien kalojen määrää voidaan lisätä
let maxFishCount = 5; // Esimerkki: Enimmillään ruudulla voi olla 5 kalaa samanaikaisesti

var gameOver = false; // Alustetaan peli lopetetuksi

var score = 0; // Alustetaan pistemäärä nollaksi

// NOTICE lisätty muuttuja, johon ChatGPT on viitannut luomassaan koodissa
let normalSpeed = 1

// NOTICE lisätty muuttuja, johon ChatGPT on viitannut luomassaan koodissa
let time = 0

// Alkuperäinen pelaajan kalan position arvo
var initialPlayerPosition = 150;

var fishCount = 0; // Alustetaan generoitujen kalojen määrä nollaksi

// Pelaajan kalan tiedot
var playerFish = {
  id: 1,
  // NOTICE lisätty muuttuja ChatGPT'n muuttuja viittauksen pohjalta
  positionX: 40, // NOTICE arvoa muutettu, ettei kala ole pelialueen reunassa kiinni
  positionY: initialPlayerPosition,
  position: 30,
  level: 1,
  color: "orange",
  // NOTICE nopeus arvo muutettu takaisin alkuperäiseen kuosiinsa (Viimeisin keskustelu, versio v0.2.1)
  speed: normalSpeed,
  width: 50, // Kalan leveys
  height: 30, // Kalan korkeus
  eye: {
    x: 38,
    y: -5,
    radius: 5
  },
  pupil: {
    x: 1,
    y: 0,
    radius: 2.5
  }
};

const fishColorsData = [
  {
    color: "red",
    width: 56,
    height: 36,
    eye: { x: 12, y: -5, radius: 6},
    pupil: { x: -1, y: 0, radius: 3},
    onCollision: (playerFish) => {
      // Tämä funktio kutsutaan, kun pelaajan kala törmää punaiseen kalaan
      // Voit toteuttaa tässä vaikutuksen, jonka punainen kala aiheuttaa
      // Esim. vähennä pelaajan elämiä tai lisää pelin nopeutta
      console.log("Peli päättyi! Punainen kala osui pelaajan kalaa.");
      gameOver = true;
    },
  },
  // Lisää muut kalalajit ja niiden vaikutusfunktiot tarpeen mukaan
  {
    color: "yellow",
    width: 37,
    height: 24,
    eye: { x: 11, y: -3, radius: 4},
    pupil: { x: -1, y: 0, radius: 2},
    onCollision: (fish) => {  // NOTICE argumentti muutettu (playerFish -> fish)
      console.log("Pelaaja sai pisteen!");
      // NOTICE funktio kutsu lisätty
      addPoints(1)
      
      // Poista kala fishList-listalta
      var fishIndex = fishList.indexOf(fish);
      if (fishIndex !== -1) {
        fishList.splice(fishIndex, 1);
      }
  },
  },
    {
    color: "blue",
    width: 26,
    height: 26,
    // NOTICE kupla ei tarvitse silmään liittyviä tietoja
    // eye: { x: 15, y: 10, radius: 5 },
    // pupil: { x: 16, y: 10, radius: 2 },
    onCollision: (fish) => {  // NOTICE argumentti muutettu (playerFish -> fish)
      console.log("Pelaaja sai kolme pistettä kuplasta!");
      // NOTICE funktio kutsu lisätty
      addPoints(3)

      // Poista kupla fishList-listalta
      var fishIndex = fishList.indexOf(fish);
      if (fishIndex !== -1) {
        fishList.splice(fishIndex, 1);
      }
    },
    // Arvotaan nopeus välillä 1-5
    getSpeed: () => {
      // NOTICE painotuksia muutettu   
      const speeds = [1.5, 1.0, 0.8];
      const probabilities = [0.7, 0.2, 0.1];

      // Arvotaan satunnainen indeksi painotetulla todennäköisyydellä
      const random = Math.random();
      let cumulativeProbability = 0;
      for (let i = 0; i < speeds.length; i++) {
      cumulativeProbability += probabilities[i];
      if (random < cumulativeProbability) {
      return speeds[i];
      }
  }

  // Oletuksena palautetaan nopeus 1.2, jos kaikki todennäköisyydet ovat nollia
  return 1.2;
    },
  },
];

// Luo uuden kalalistan, johon lisätään muut kalaobjektit
var fishList = [];

// NOTICE funktio lisätty ChatGPT'n viittauksen perusteella
function getVersion() {
    return gameVersion
}

// Aloita peli
function startGame() {
  // NOTICE Muuttujat on säästetty funktion edellisestä versiosta
  fishList = [];
  score = 0;
 
  fishCount = 0; // Nollaa generoitujen kalojen määrä
 
  // NOTICE lisätty nollauksia
  gameDifficulty = 0;
  maxFishCount = 5;
  normalSpeed = 1;
  time = 0
 
  // Palauta pelaajan kalan position alkuperäiseen arvoon
  playerFish.positionY = initialPlayerPosition;

  // Päivitä pistemäärän näyttö
  document.getElementById("score-display").textContent = score;
 
  canvas.removeEventListener("click", startGame);
  gameOver = false;
  updateGame();
}

// Funktio, joka lisää pelaajalle pisteitä
function addPoints(points) {
  score += points;
  // Voit tässä päivittää myös näytöllä näkyvää pistetilannetta tarvittaessa
}

//
function getRandomColor() {
  const colors = ["red", "yellow", "blue"];

  // Muokkaa todennäköisyyslistaa vaikeustason perusteella
  let probabilities;
 
  if (gameDifficulty >= 4) {
    const redProbability = 0.45 + (gameDifficulty - 4) * 0.02; // Punaisen todennäköisyys nousee 1-2% jokaisella vaikeustason nousulla
    const yellowProbability = 0.45 - (gameDifficulty - 4) * 0.02; // Keltaisen todennäköisyys laskee 1-2% jokaisella vaikeustason nousulla
    probabilities = [redProbability, yellowProbability, 0.1]; // Sininen pysyy samana (10%)
  } else if (gameDifficulty >= 2) {
    probabilities = [0.4, 0.45, 0.15]; // Punainen 40%, keltainen 45%, sininen 15%
  } else if (gameDifficulty >= 1) {
    probabilities = [0.35, 0.65, 0]; // Punainen 35%, keltainen 65%, sininen 0%
  } else {
    probabilities = [0.25, 0.75, 0]; // Punainen 25%, keltainen 75%, sininen 0%
  }

  // Arvotaan satunnainen indeksi painotetulla todennäköisyydellä
  const random = Math.random();
  let cumulativeProbability = 0;
  for (let i = 0; i < colors.length; i++) {
    cumulativeProbability += probabilities[i];
    if (random < cumulativeProbability) {
      // console.log(random + " - " + cumulativeProbability + " = " + colors[i] + " (" + i +")" + "(" + gameDifficulty +")")
      return colors[i];
    }
  }
 
  console.log("Kalaa ei arvottu? " + random)
  // Oletuksena palautetaan sininen, jos kaikki todennäköisyydet ovat nollia (esim. vaikeustasolla 0)
  return "yellow";
}

// NOTICE Joitakin osia tuotu vanhasta generaattorista
function generateFish() {
  // Arvo satunnainen väri
  const randomColor = getRandomColor()

  // Etsi vastaava kala-objekti fishColorsData-listasta
  const fishData = fishColorsData.find((fish) => fish.color === randomColor);
 
  if (fishData) {
    // Jos löydettiin kala-objekti, käytä getSpeed-funktiota tai arvo normaali nopeus
    const speed = fishData.getSpeed ? fishData.getSpeed() : Math.random() < 0.12 ? 1.5 : 1; // getRandomNumber(1, 5);
    
    // Generoidaan kalan sijainti ja muut ominaisuudet
    const positionX = gameWidth;
    //NOTICE Arvontaa muutettu kalan piirron muutoksen vuoksi + sijanti tiedot siirretty myöhemmälle kalojen vaihtelevan koon vuoksi
    const positionY = Math.floor(Math.random() * (gameHeight - fishData.height) + fishData.height / 2); // Satunnainen korkeus (20-260); // Arvo satunnainen sijainti pystysuunnassa
    
    const newFish = {
      // DEBUG pidetään toistaiseksi kalan ominaisuutena 
      level: Math.floor(Math.random() * playerFish.level) + 1, // Satunnainen taso (1-pelaajan taso)
        
      id: fishCount + 2, // Lisää 2 id:hen, jotta huomioi pelaajan kalan
      color: fishData.color,
      positionX,
      positionY,
      speed,
      width: fishData.width,
      height: fishData.height,
      eye: { ...fishData.eye }, // Kopioidaan silmä-objekti uuteen kalahaamuun
      pupil: { ...fishData.pupil }, // Kopioidaan pupilli-objekti uuteen kalahaamuun
      onCollision: fishData.onCollision,
      // Lisää muut kalan ominaisuudet tarpeen mukaan
    };
    
    fishList.push(newFish); // Lisää uusi kalaobjekti kalalistaan
    fishCount++; // Kasvata generoitujen kalojen määrää yhdellä
  } else {
      console.log("Arvottua kalaa ei löytynyt!")
  }
}

// Määritä pistemäärärajat eri vaikeustasoille
const difficultyThresholds = {
  1: 10, // Esimerkki: 10 pistettä vaaditaan vaikeustason 1 saavuttamiseen
  2: 25, // Esimerkki: 20 pistettä vaaditaan vaikeustason 2 saavuttamiseen
  3: 40  // Esimerkki: 30 pistettä vaaditaan vaikeustason 3 saavuttamiseen
};

// Päivitä pelin vaikeustaso ja pelinopeus pelaajan pisteiden perusteella
function updateGameDifficulty(score) {
  // Tarkista, onko saavutettu uusi vaikeustaso
  for (const difficultyLevel in difficultyThresholds) {
    if (score >= difficultyThresholds[difficultyLevel] && difficultyLevel > gameDifficulty) {
      // Päivitä vaikeustaso ja pelinopeus
      // NOTICE muuteettu nostamaan vaikeus tasoa
      gameDifficulty += 1 // = parseInt(difficultyLevel);
      normalSpeed += 0.5; // Esimerkki: Kasvata pelinopeutta 0.5 yksikköä uutta vaikeustasoa kohti
      console.log(`Uusi vaikeustaso: ${gameDifficulty}`);
      break; // Poistu silmukasta, kun ensimmäinen uusi vaikeustaso on löytynyt
    }
  }
}

function checkCollision_OLD(playerFish, fish) {   
  // Tarkista, osuvatko kalojen rajat toisiinsa
  // NOTICE törmäys tarkastusta hienosäädetty
  if (
    // playerFish.positionX < fish.positionX + fish.width && // alkuperäinen
    // NOTICE kalojen törmäystä säädetty
    playerFish.positionX + playerFish.width / 2 < fish.positionX + fish.width / 2 &&
    playerFish.positionX + playerFish.width > fish.positionX &&
    playerFish.positionY < fish.positionY + fish.height  &&
    playerFish.positionY + playerFish.height > fish.positionY
  ) {
    return true; // Osuvat yhteen
  }

  return false; // Eivät osu yhteen
}

function checkCollision(playerFish, fish) {
  // Lasketaan kalojen keskipisteiden välinen etäisyys
  // NOTICE laskentaa korjattu
  const distanceX = (playerFish.positionX + playerFish.width - playerFish.height / 2) - (fish.positionX + fish.height/ 2);
  const distanceY = (playerFish.positionY) - (fish.positionY);
  const distance = Math.sqrt(distanceX ** 2 + distanceY ** 2);

  // Lasketaan pelaajan ohjaaman kalan säde
  const playerFishRadius = playerFish.height / 2;

  // Lasketaan viholliskalan säde
  const fishRadius = fish.height / 2;

  // Tarkistetaan, osuvatko kalojen rajat toisiinsa
  if (distance < playerFishRadius + fishRadius) {
    return true; // Osuvat yhteen
  }

  return false; // Eivät osu yhteen
}

// Pelin päätyttyä tarkista tulosennätys
// NOTICE funktion nimi muutettu sekannuksen välttämiseksi
function updateGameOver() {
  // NOTICE funktio kutsu muutettu muuttuja kutsuksi   
  const finalScore = score; // Oletetaan, että game.js-tiedostossa on metodi getScore() palauttamaan pelaajan pisteet
  const highScore = checkHighScore(finalScore);
  saveHighScore(highScore);
  updateHighScoreElement();
  // Muut pelin lopettamiseen liittyvät toimenpiteet...
}

// Päivittää pelitapahtumat ja liikuttaa kaloja
function updateGame() {
  // Päivitä muut kalaobjektit
  for (var i = 0; i < fishList.length; i++) {
    var fish = fishList[i];
    // NOTICE Kalan "fish.level" ominaisuus vaihdettu pelin perus nopeuteen "normalSpeed"
    fish.positionX -= normalSpeed * fish.speed * 2;
    
    // *** Lisätty ChatGPT'n luomaa koodia
    // Tarkista, onko kala edennyt pelialueen reunan ohi
    if (fish.positionX < -fish.width) {
      // Poista kala listalta
      fishList.splice(i, 1);
    }
    
    if (checkCollision(playerFish, fish)) {
      console.log(fish)
      fish.onCollision(fish); // Suoritetaan kalan "onCollision" -funktio törmäyksen tapahtuessa
      // Tässä voit tehdä muutakin logiikkaa, jos tarpeen
      document.getElementById("score-display").textContent = score;
    }
  }
 
  // NOTICE funktio kutsu lisätty
  renderGame(fishList, playerFish)
  
  // NOTICE muuttuja viittausta muokattu vastaamaan aiempaa koodia
  updateGameDifficulty(score);

  // Generoi uusi kala satunnaisesti
  // NOTICE generointi tiheyttä muutettu
  if (Math.random() < 0.013) { // Voit säätää generointitiheyttä muuttamalla lukua
    //  && fishList.length < maxFishCount ChatGPT'n luomaa ominaisuutta ei vielä lisätty
    generateFish();
  }

  // Tarkista, onko peli päättynyt
  if (!gameOver) {
    requestAnimationFrame(updateGame);
  } else {
    // NOTICE drawGameOver metodin paikkaa muutettu
    drawGameOver();
    updateGameOver()
  }
 
  time += 1 * normalSpeed;
}

function startMoving(direction) {
  // Tarkista, että liike ei ole jo käynnissä
  if (!movementInterval) {
    movementInterval = setInterval(function () {
      // Päivitä pelaajan kalan sijaintia haluttuun suuntaan
      updatePlayerFishPosition(direction);
    }, 10); // Päivitä liike pienellä aikavälillä (esim. 10 ms)
  }
}

function stopMoving() {
  // Pysäytä liike ja tyhjennä interval-muuttuja
  clearInterval(movementInterval);
  movementInterval = null;
}

document.addEventListener("keydown", function (event) {
  // Tarkista, minkä nuolinäppäimen tapahtuma laukaisi
  switch (event.keyCode) {
    case 38: // Nuolinäppäin ylös
      startMoving("up");
      break;
    case 40: // Nuolinäppäin alas
      startMoving("down");
      break;
  }
});

document.addEventListener("keyup", function (event) {
  // Tarkista, minkä nuolinäppäimen tapahtuma laukaisi
  switch (event.keyCode) {
    case 38: // Nuolinäppäin ylös
    case 40: // Nuolinäppäin alas
      stopMoving();
      break;
  }
});

// NOTICE Muutettu "playerFish.position" viittaus muotoon "playerFish.positionY"
function updatePlayerFishPosition(direction) {
  // Päivitä pelaajan kalan sijaintia haluttuun suuntaan
  if (direction === "up") {
    playerFish.positionY -= playerFish.speed;
  } else if (direction === "down") {
    playerFish.positionY += playerFish.speed;
  }

  // Varmista, että pelaajan kala pysyy pelialueella
  // NOTICE pelaajan kalan liikettä säädetty piirrossa tehdyn muutoksen vuoksi
  if (playerFish.positionY < playerFish.height / 2) {
    playerFish.positionY = playerFish.height / 2;
  } else if (playerFish.positionY > gameHeight - playerFish.height / 2) {
    playerFish.positionY = gameHeight - playerFish.height / 2;
  }
}

// NOTICE lisätty funktio kutsut jo olemassa oleviin funktioigin
// Kuunnellaan napin painalluksia
document.getElementById('move-up').addEventListener('mousedown', function() {
  // moveDirection = 1; // Pelaaja liikkuu ylös kun painetaan
  startMoving("up");
});
document.getElementById('move-up').addEventListener('mouseup', function() {
  // moveDirection = 0; // Pelaaja pysähtyy kun päästetään nappi
  stopMoving();
});

document.getElementById('move-down').addEventListener('mousedown', function() {
  // moveDirection = -1; // Pelaaja liikkuu alas kun painetaan
  startMoving("down");
});
document.getElementById('move-down').addEventListener('mouseup', function() {
  // moveDirection = 0; // Pelaaja pysähtyy kun päästetään nappi
  stopMoving();
});

// Tämä lisää tapahtumakuuntelijan "touchstart" -tapahtumalle, joka toimii mobiililaitteilla
document.getElementById('move-up').addEventListener('touchstart', () => {
  startMoving("up");
});

// Tämä lisää tapahtumakuuntelijan "touchstart" -tapahtumalle, joka toimii mobiililaitteilla
document.getElementById('move-down').addEventListener('touchstart', () => {
  startMoving("down");
});

// Tämä lisää tapahtumakuuntelijan "touchstart" -tapahtumalle, joka toimii mobiililaitteilla
document.getElementById('move-up').addEventListener('touchend', () => {
  stopMoving();
});

// Tämä lisää tapahtumakuuntelijan "touchstart" -tapahtumalle, joka toimii mobiililaitteilla
document.getElementById('move-down').addEventListener('touchsend', () => {
  stopMoving();
});

// Kutsu drawGameOver-funktiota pelin alussa
// NOTICE "updateHighScore" -funktio kutsu lisätty
updateHighScoreElement()
drawStartGame();

JavaScript:
// NOTICE siirretty game.js tiedostosta
var canvas = document.getElementById("game-canvas");
var ctx = canvas.getContext("2d");

var gameWidth = canvas.width;
var gameHeight = canvas.height;

function drawBackground(ctx, canvasWidth, canvasHeight) {
  // Luodaan sinisen liukuvärin alkaen pohjasta (tummempi) ja päättyen yläosaan (vaaleampi)
  const gradient = ctx.createLinearGradient(0, gameHeight, 0, 0);
  gradient.addColorStop(0, "#003366"); // Tumma sininen
  gradient.addColorStop(1, "#66ccff"); // Vaalea sininen

  // Piirretään liukuväri taustalle
  ctx.fillStyle = gradient;
  ctx.fillRect(0, 0, gameWidth, gameHeight);
 
  // Piirrä pohjan hiekanvärisen alueen alareuna
  // NOTICE "context" muuttuja viittaukset vaihdettu muotoon "ctx"
  // ctx.fillStyle = "sandybrown";
  // ctx.fillRect(0, gameHeight - 10, gameWidth, 10);
  drawSand3(time)
 
  // NOTICE lisätty viittaus funktioon
  drawGameVersion();
}

// NOTICE parametrit muutettu
function drawSand3(time = 0) {
  const sandHeight = gameHeight * 0.12; // Voit muuttaa hiekkapohjan korkeutta täällä
  const sandWidth = gameWidth * 1.2; // Muuta tätä arvoa tarvittaessa
 
  // Luodaan liukuvärjätty alue
  const gradient = ctx.createLinearGradient(0, gameHeight, 0, gameHeight - sandHeight);
  gradient.addColorStop(0, "SaddleBrown"); // Yläreuna väri (sandybrown)
  gradient.addColorStop(1, "sandybrown"); // Alareuna väri (white)

  // Piirrä liukuvärjätty alue
  ctx.fillStyle = gradient;
  ctx.fillRect(0, gameHeight - sandHeight, gameWidth, sandHeight);

  // Piirrä liukuvärjätty alue
  ctx.fillStyle = gradient;
  ctx.fillRect(-sandWidth / 2, gameHeight - sandHeight, sandWidth, sandHeight);

  // Aaltoileva yläreuna
  const waveHeight = 5;
  const waveLength = 100;
  const yOffset = 2 * Math.sin(time / 100);

  ctx.beginPath();
  ctx.moveTo(-sandWidth / 2, gameHeight - sandHeight);
  for (let x = -sandWidth / 2; x < gameWidth + sandWidth / 2; x += 10) {
    const y = gameHeight - sandHeight + waveHeight * Math.sin((x + time) / waveLength) + yOffset;
    ctx.lineTo(x, y);
  }
  ctx.lineTo(gameWidth + sandWidth / 2, gameHeight - sandHeight);
  ctx.closePath();
 
  ctx.fillStyle = "peru"; // (sandybrown) -> (Chocolate)
  ctx.fill();
}

// NOTICE pelin versionumeron piirtämisestä tehty funktio
// NOTICE piirtämistä fixattu hieman, väri, fontti koko ja kohta
// NOTICE "context" muuttuja viittaukset vaihdettu muotoon "ctx"
function drawGameVersion() {
  // Piirrä pelin versionumero
  const version = getVersion(); // Oletetaan, että game.js-tiedostossa on metodi getVersion() palauttamaan versionumeron

  ctx.fillStyle = "Black";
  ctx.font = "10px Arial";
  ctx.fillText(version, gameWidth - 20, 10);
}

// NOTICE drawGameStart ja drawGameOver funktiot siirretty game.js -tiedostosta
// *** Lisätty ChatGPT'n luomaa koodia
// Piirrä "Aloita peli" -nappi
function drawStartGame() {
  ctx.clearRect(0, 0, canvas.width, canvas.height); // Tyhjennä canvas

  ctx.fillStyle = "#4CAF50";
  ctx.fillRect(canvas.width / 2 - 80, canvas.height / 2 - 25, 160, 50);

  ctx.font = "20px Arial";
  ctx.fillStyle = "white";
  ctx.textAlign = "center";
  ctx.fillText("Aloita peli", canvas.width / 2, canvas.height / 2 + 8);

  canvas.addEventListener("click", startGame);
}

// Piirrä "Peli loppui" -viesti ja "Aloita peli" -nappi
function drawGameOver() {
  ctx.save(); // Tallenna piirtotila

  ctx.globalAlpha = 0.5; // Aseta läpinäkyvyys
  ctx.fillStyle = "black";
  ctx.fillRect(0, 0, canvas.width, canvas.height);

  ctx.globalAlpha = 1; // Palauta täysi läpinäkyvyys
  ctx.font = "30px Arial";
  ctx.fillStyle = "white";
  ctx.textAlign = "center";
  ctx.fillText("Peli loppui!", canvas.width / 2, canvas.height / 2 - 20);

  ctx.fillStyle = "#4CAF50";
  ctx.fillRect(canvas.width / 2 - 80, canvas.height / 2 + 10, 160, 50);

  ctx.font = "20px Arial";
  ctx.fillStyle = "white";
  ctx.fillText("Aloita peli", canvas.width / 2, canvas.height / 2 + 40);

  ctx.restore(); // Palauta piirtotila

  canvas.addEventListener("click", startGame);
}

// NOTICE "playerFish" muuttuja viittaukset muutettu "fish" -muotoon, kuten muissakin kaloissa
function drawPlayerFish(fish) {
  ctx.fillStyle = playerFish.color; // Aseta pelaajan kalan väri (esim. oranssi)
 
  // Pyrstö
  ctx.beginPath();
  ctx.arc(fish.positionX, fish.positionY, fish.height / 2,  Math.PI / 2 , Math.PI + Math.PI / 2, true);
  ctx.closePath();
  ctx.fill();
 
  // Lasketaan etäisyys keskeltä kärkeen
  const sideLength = 14 // (fish.height / 2) / Math.cos(Math.PI / 6);
 
  ctx.beginPath();
  ctx.moveTo(fish.positionX + fish.width - fish.height / 2, fish.positionY - fish.height / 2);

  ctx.arc(fish.positionX + fish.width - fish.height / 2, fish.positionY, fish.height / 2, Math.PI * 1.5, Math.PI * 0.5, false);
  // NOTICE lisätty
  ctx.lineTo(fish.positionX + fish.width - sideLength - sideLength / 2, fish.positionY + fish.height / 2);
  ctx.arc(fish.positionX + fish.width - sideLength - sideLength / 2, fish.positionY, fish.height / 2,  Math.PI * 0.5, Math.PI * 1.5, false);
  ctx.closePath();
  ctx.fill();
 
  // Piirrä silmä
  ctx.fillStyle = "white"; // Silmän väri
  ctx.beginPath();
  ctx.arc(
    fish.positionX + fish.eye.x,
    fish.positionY + fish.eye.y,
    fish.eye.radius,
    0,
    2 * Math.PI
  );
  ctx.fill();

  // Piirrä pupilli
  ctx.fillStyle = "black"; // Pupillin väri
  ctx.beginPath();
  ctx.arc(
    fish.positionX + fish.eye.x + fish.pupil.x,
    fish.positionY + fish.eye.y + fish.pupil.y,
    fish.pupil.radius,
    0,
    2 * Math.PI
  );
  ctx.fill();
}

function drawRedFish(fish) {
  ctx.fillStyle = "red";

  // Pyrstö
  ctx.beginPath();
  ctx.moveTo(fish.positionX + fish.width, fish.positionY - fish.height / 2);
  ctx.lineTo(fish.positionX + fish.width - fish.height * 0.6, fish.positionY);
  ctx.lineTo(fish.positionX + fish.width, fish.positionY + fish.height / 2)
  ctx.closePath();
  ctx.fill();

  // Kala
  let cornerRadius = 12;
 
  ctx.beginPath();
  ctx.moveTo(fish.positionX + cornerRadius, fish.positionY - fish.height / 2);
  ctx.arcTo(fish.positionX, fish.positionY  - fish.height / 2, fish.positionX, fish.positionY + cornerRadius, cornerRadius);
  ctx.lineTo(fish.positionX, fish.positionY + fish.height / 2 - cornerRadius);
  ctx.arcTo(fish.positionX, fish.positionY + fish.height / 2, fish.positionX + cornerRadius, fish.positionY + fish.height / 2, cornerRadius);
  ctx.lineTo(fish.positionX + cornerRadius + 15, fish.positionY + fish.height / 2);
  //ctx.lineTo(x + width, y);
  // NOTICE lisätty puoli pyöreä takaosa
  // ctx.arcTo(fish.positionX + cornerRadius * 2 + 15, fish.positionY + fish.height, fish.positionX + cornerRadius * 3 + 15, fish.positionY + fish.height / 2, fish.height / 2);
  // ctx.arcTo(fish.positionX + cornerRadius * 3 + 15, fish.positionY, fish.positionX + cornerRadius * 2 + 15 , fish.positionY, fish.height / 2);
  ctx.arc(fish.positionX + cornerRadius + 15, fish.positionY, fish.height / 2, Math.PI * 1.5, Math.PI * 0.5, false);
  ctx.lineTo(fish.positionX + cornerRadius + 15, fish.positionY - fish.height / 2);
  ctx.closePath();
  ctx.fill();
 
  // Piirrä silmä
  ctx.fillStyle = "hotpink"; // Silmän väri
  ctx.beginPath();
  ctx.arc(
    fish.positionX + fish.eye.x,
    fish.positionY + fish.eye.y,
    fish.eye.radius,
    0,
    2 * Math.PI
  );
  ctx.fill();

  // Piirrä pupilli
  ctx.fillStyle = "black"; // Pupillin väri
  ctx.beginPath();
  ctx.arc(
    fish.positionX + fish.eye.x + fish.pupil.x,
    fish.positionY + fish.eye.y + fish.pupil.y,
    fish.pupil.radius,
    0,
    2 * Math.PI
  );
  ctx.fill();
}

function drawYellowFish(fish) {
  ctx.fillStyle = "yellow";
 
  // Pyrstö
  ctx.beginPath();
  ctx.arc(fish.positionX + fish.width, fish.positionY, fish.height / 2,  Math.PI / 2 , Math.PI + Math.PI / 2, false);
  ctx.closePath();
  ctx.fill();
 
  // Kala 
  // Lasketaan etäisyys keskeltä kärkeen
  const side = (fish.height / 2) / Math.cos(Math.PI / 6);
  // console.log(side); // Tulostaa etäisyyden keskeltä kärkeen       

  const sideLength = (fish.height / 2) / Math.cos(Math.PI / 6);
 
  ctx.beginPath();
  ctx.moveTo(fish.positionX + sideLength / 2, fish.positionY - fish.height / 2);
  ctx.lineTo(fish.positionX + sideLength + sideLength / 2, fish.positionY - fish.height / 2);
  ctx.lineTo(fish.positionX + sideLength * 2, fish.positionY);
  ctx.lineTo(fish.positionX + sideLength + sideLength / 2, fish.positionY + fish.height / 2);
  ctx.lineTo(fish.positionX + sideLength / 2, fish.positionY + fish.height / 2);
  ctx.lineTo(fish.positionX, fish.positionY);
 
  ctx.closePath();
  ctx.fill();
 
  // Piirrä silmä
  ctx.fillStyle = "white"; // Silmän väri
  ctx.beginPath();
  ctx.arc(
    fish.positionX + fish.eye.x,
    fish.positionY + fish.eye.y,
    fish.eye.radius,
    0,
    2 * Math.PI
  );
  ctx.fill();

  // Piirrä pupilli
  ctx.fillStyle = "black"; // Pupillin väri
  ctx.beginPath();
  ctx.arc(
    fish.positionX + fish.eye.x + fish.pupil.x,
    fish.positionY + fish.eye.y + fish.pupil.y,
    fish.pupil.radius,
    0,
    2 * Math.PI
  );
  ctx.fill();
}

// NOTICE "ctx" -parametri poistettu
function drawBubble(bubble) {
  ctx.fillStyle = "blue";
  ctx.beginPath();
  ctx.arc(
    // bubble.positionX + (bubble.width / 4), // piirretään kupla hieman eteen sijainnista, osumisen parantamisen vuoksi
    bubble.positionX,
    // bubble.positionY + bubble.height / 2, // NOTICE kupla siirretty korkeus suunnasssa (jotta törmäys voidaan laskea oikein)
    bubble.positionY,
    // NOTICE "radius" ominaisuus muutettu "height" muotoon
    bubble.height / 2,
    0,
    Math.PI * 2,
    false
  );
  ctx.closePath();
  ctx.fill();
}

// NOTICE kalan ominaisuus "type" muutettu muotoon "color"
function drawFishList(fishList) {
  fishList.forEach((fish) => {
    if (fish.color === "red") {
      drawRedFish(fish);
    } else if (fish.color === "yellow") {
      drawYellowFish(fish);
    // NOTICE muutettu vastaamaan "fish" objektissa olevaa väriä
    } else if (fish.color === "blue") {
      drawBubble(fish);
    }
  });
}
// ***

function drawFish(fish){
    // Piirrä kala
    ctx.fillStyle = fish.color;
    // NOTICE kalan kokoa muutettu
    ctx.fillRect(canvas.width - fish.position, fish.height, 50, 30);
}

// *** Lisätty ChatGPT'n luomaa koodia
// Piirtämiseen liittyvä funktio
function renderGame(fishList, playerFish) {
  // Tyhjennä canvas
  ctx.clearRect(0, 0, canvas.width, canvas.height);
 
  // Piirrä pelialueen tausta
  // NOTICE funktiokutsu lisätty
  drawBackground(ctx, gameWidth, gameHeight)
 
  // Piirrä kalat
  drawFishList(fishList); // Piirrä kaikki kalat

  // Piirrä pelaajan ohjaama kala
  drawPlayerFish(playerFish);
  // Piirrä muut tarvittavat visuaaliset elementi
}

Pelissä on toki muitakin kehityskohtia, myös niitä saa ja kannattaa tuoda esille.

edit. pelin koodit liitetty viestiin.
 
Viimeksi muokattu:
Tulee lähinnä mieleen että joku object oriented tyylinen ratkaisu voisi olla hyvä.
 
Hahmottelin hieman luokkaa (heti syvään päätyyn, perintä). Tämä helpottanee jatkossa myös uusien hahmojen ja elementtien tuomista peliin. Oma lukunsa on yhdistää tämä olemassa olevaan koodiin. Ennen sitä täytyy vähän työstää vielä ominaisuuksia (mm. jostain syystä korkeuden arvonta antaa vain "NaN" -arvon).

JavaScript:
const fishCount = 6
const gameWidth = 600
const gameHeight = 400
let gameOver = false

class Fish {
  constructor(color) {
    this.id = fishCount + 2
    this.color = color;
    this.speed = Math.random() < 0.12 ? 1.5 : 1; // getRandomNumber(1, 5);

    const positionY = Math.floor(Math.random() * (gameHeight - this.height) + this.height / 2); // Arvo satunnainen sijainti pystysuunnassa
    console.log("postitionY: ", positionY,"height: " , this.height)
    this.position = {x: gameWidth, y: positionY}
  }
 
  onCollision(playerFish) {
    console.log("Kalojen yheen törmäykselle ei määritelty toimintoa.");
  }

  static _randomSpeed() {
    return Math.random() * 10;
  }

  updatePosition() {
    this.position.x += this.speed;
    this.position.y += this.speed;
  }
}

class RedFish extends Fish { 
  width = 56
  height = 36
 
  constructor() {
    super("red");
    this.speed = this.randomSpeedGray();
  }
 
  onCollision(playerFish) {
      console.log("Peli päättyi! Punainen kala osui pelaajan kalaan.");
      gameOver = true;
  }

  randomSpeedGray() {
    return Math.random() * 5;
  }
}

const redFish = new RedFish();
redFish.updatePosition();
console.log("Red Fish Position:", redFish.position.x, redFish.position.y, redFish.speed);
redFish.onCollision()

Olisiko tuo kalan piirtämiseen käytettävä funktio hyvä sisällyttää myös tähän luokkaan, vaikka piirtäminen onkin renderin vastuulla?
 
onCollision-metodi: RedFish-luokassa onCollision-metodi kutsuu gameOver-muuttujaa, mutta playerFish-parametria ei käytetä. Voit lisätä tarkistuksen, jos haluat käyttää playerFish-parametria.

JavaScript:
const fishCount = 6;
const gameWidth = 600;
const gameHeight = 400;
let gameOver = false;

class Fish {
    constructor(color) {
        this.id = fishCount + 2;
        this.color = color;
        this.speed = Math.random() < 0.12 ? 1.5 : 1;
        this.height = 36; // Oletuskorkeus

        const positionY = Math.floor(Math.random() * (gameHeight - this.height) + this.height / 2);
        console.log("positionY: ", positionY, "height: ", this.height);
        this.position = { x: gameWidth, y: positionY };
    }

    onCollision(playerFish) {
        console.log("Kalojen yheen törmäykselle ei määritelty toimintoa.");
    }

    static _randomSpeed() {
        return Math.random() * 10;
    }

    updatePosition() {
        this.position.x -= this.speed; // Liikkuu vasemmalle
    }
}

class RedFish extends Fish {
    width = 56;
    height = 36;

    constructor() {
        super("red");
        this.speed = this.randomSpeedGray();
    }

    onCollision(playerFish) {
        console.log("Peli päättyi! Punainen kala osui pelaajan kalaan.");
        gameOver = true;
    }

    randomSpeedGray() {
        return Math.random() * 5;
    }
}

const redFish = new RedFish();
redFish.updatePosition();
console.log("Red Fish Position:", redFish.position.x, redFish.position.y, redFish.speed);
redFish.onCollision();
 

Statistiikka

Viestiketjuista
258 201
Viestejä
4 489 439
Jäsenet
74 169
Uusin jäsen
tater

Hinta.fi

Back
Ylös Bottom