質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.57%

【js】親オブジェクトのプロパティを使いたい

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 2,900

mijinko889

score 33

前提

javascriptで記述した以下のようなコードがあるとします。
制約条件
・パーティに同じ名前のキャラは一人しか編成できない
・パーティには控えはいない
・キャラは違う処理をするスキルを持つ
・ダメージを受けた時特有の反応をするキャラもいる

characters ={
    "主人公":{
        partyid:0,
        AttackDate:{
          discription:"ターゲットに通常攻撃",
          count:10,
          Atk:function(){
            getDamage = partystetas[characters["主人公"].partyid].Pow;
            Damage(standEnemy[characters["主人公"].partyid],getDamage);
            //console.log("aaa");
            EnemySetting(characters["主人公"].partyid);
            console.log(enemystetas.length);
          },
        },
        SapportDate:{
          discription:"SCORE上昇量1%up",
          Sap:function(){
              if(updatecounter % fps_*3 === 0){
                for(var i=0;i<4;i++){
                if(partystetas[i].Hp < partystetas[i].maxHP)
                    partystetas[i].Hp += 50;
                  }
              }
          }
        },
        Dam:function(){
          Damage(this.partyid,getDamage);
        },
        Date:new CharacterData(
          "img/characters/sprite_char1.jpg",//イメージuri
          0,//ID
          "主人公",//表示名--パーティーデータ取得時に主キーにしてユーザー毎のキャラステータスを取得?
          1100,//最大HP
          1500,//攻撃力
          800,//防御力
          "atk",//タイプ
          "ヒューマン",//種族
          "SR"//レアリティ
        ),
  },
  "朱雀":{
    partyid:0,
    AttackDate:{
      discription:"ターゲットに通常攻撃",
      count:10,
      Atk:function(){
      },
    },
    SapportDate:{
      discription:"SCORE上昇量1%up",
      Sap:function(){
        if(updatecounter % fps_*5 === 0){
          score_ += 10;
        }
      }
    },
    Dam:function(){
      Damage(this.partyid,getDamage);
    },
      Date:new CharacterData(
        "img/characters/sprite_char2.jpg",
        1,
        "ヒロイン",
        1010,
        1020,
        200,
        "enh",
        "ヒューマン",
        "SR"
      ),
},
  "白虎":{
    partyid:0,
    AttackDate:{
      discription:"ターゲットに通常攻撃",
      count:10,
      Atk:function(){
        console.log("aaap");
      },
    },
    SapportDate:{
      discription:"SCORE上昇量5%up",
      Sap:function(){
        if(updatecounter % fps_*3 === 0)
          member_combCount[characters["プレイヤー"].partyid]+=1;
      }
    },
    Dam:function(){
      Damage(this.partyid,getDamage);
    },
      Date:new CharacterData(
        "img/characters/sprite_char3.jpg",
        1,
        "プレイヤー",
        1010,
        1020,
        200,
        "enh",
        "ヒューマン",
        "SR"
      ),
  },
  "玄武":{
    partyid:0,
    AttackDate:{
      discription:"ターゲットに通常攻撃",
      count:10,
      Atk:function(){
        console.log("捕ったど~");
      },
    },
    SapportDate:{
      discription:"SCORE上昇量5%up",
      Sap:function(){
        //console.log("Support3");
      }
    },
    Dam:function(){
      console.log("ダメージ受けたなり~");
      Damage(this.partyid,getDamage);
    },
      Date:new CharacterData(
        "img/characters/sprite_char3.jpg",
        1,
        "玄武",
        1300,
        1520,
        300,
        "def",
        "オーガ",
        "SSR"
      ),
  },
};

function EnemySetting(num){
  if(standEnemy[num].Hp <= 0){
    if(enemystetas.length > 0){
      standEnemy[num]=(enemystetas.shift());
      standEnemy[num].partyid = num;
    }
  }
}


キャラ(charaClassオブジェクト)ごとに違うスキル、ステータスを保持しており、戦闘シーンでのメイン処理では分岐を使わず同じ呼び出し方で書きたいと思ってたどり着いたのが現状の書き方ですね。「キャラの持つスキルの内容」を一纏めにするためにAttackDateを作っています。

キャラを作成するのと同じようにスキルも作成しようと思ったのですが、引数にfunction()を渡すコールバック処理をcharacterclassオブジェクトに作っています。

var
member_size_X = member_size_Y=32,//サイズ
partyspritesize_W=288,partyspritesize_H=32,
Damagestetas={
  damage_:0,
  type_:"ATK"//攻撃者のタイプ
},
getDamage=0;
//キャラクターデータクラス
function CharacterData(img,id,name,maxhp,maxpow,maxdeff,type,race,rera){
  this.img=new Image(),
  this.img.src=img;
  this.id=id;
  this.name=name;
  this.Hp=maxhp;
  this.Pow=maxpow;
  this.Deff=maxdeff;
  this.Type=type;
  this.Race=race;
  this.Rare=rera;

  this.waitSprite = new AnimSprite(this.img,0,0,partyspritesize_W,partyspritesize_H,member_size_X,member_size_Y,3,1);
  this.attackSprite= new AnimSprite(this.img,member_size_X*3,0,partyspritesize_W,partyspritesize_H,member_size_X,member_size_Y,3,1);
  this.skillSprite= new AnimSprite(this.img,0,member_size_Y*2,partyspritesize_W,partyspritesize_H,member_size_X,member_size_Y,3,1);
  this.damageSprite= new AnimSprite(this.img,0,member_size_Y*3,partyspritesize_W,partyspritesize_H,member_size_X,member_size_Y,3,1);
  this.deadSprite= new AnimSprite(this.img,192,160,partyspritesize_W,partyspritesize_H,member_size_X,member_size_Y,3,1);

  this.motionsprite=  this.waitSprite ;

  this.Atkfunction = function(atkfunc){
      atkfunc();
    };

  this.Damagefunction=function(damagefunc){
    damagefunc()
  };

  this.Sapportfunction=function(sapportfunc){
      sapportfunc();
    };
}


//基本的なダメージ処理
var
Damage = function(member,damage){
  member.Hp -= damage;
},
//キャラ描画
Characterdtaw = function(charax_,charay_,sprite_){
  sprite_.animdraw(ctx,charax_,charay_);
};

function charinit(){//呼び出し方の例
  var
    i,

    chara1 = characters["主人公"],
    chara2 = characters["ヒロイン"];


  chara1.Date.Atkfunction(chara1.AttackDate.Atk);
  chara2.Date.Sapportfunction(chara1.SapportDate.Sap);


}

function SetDamagest(Damagest,damage_,type_){
  Damagest.damage_ = damage_;
  Damagest.type_ = type_;

  return Damagest;
}

function DamageCol(stetas_){
    var getdamage;
    getdamage = stetas_.damage_;//ダメージ計算

    return getdamage;
}
var
//////////////////////////////////////////////パーティオブジェクト/////////////////////////////////////////////////////
party=[
  characters["主人公"],
  characters["ヒロイン"],
  characters["プレイヤー"],
  characters["玄武"]
],//4人
partystetas=[],

i=0,
STEPTIME = 10,
step=0;

function Partyinit(){
  for(i=0;i<4;i++){
    party[i].partyid = i;
    partystetas[i]={
        maxHP:party[i].Date.Hp,
        Hp:party[i].Date.Hp,
        maxpow:party[i].Date.Pow*5,
        Pow:party[i].Date.Pow,
        maxDef:party[i].Date.Deff*5,
        def:party[i].Date.Deff,
        specialgage:0
    };
    memberView[i]=  {
        hpvar:document.getElementById("HPbox_member"+(i+1)),
        combcount:document.getElementById("Comb_member"+(i+1)),
        iconimg:document.getElementById("membericonimg"+(i+1)),
        Hp:document.getElementById("member"+(i+1)+"_stetas")
    };
  }
}

function getPartyMemberActionSkill(num){
  if(partystetas[num].Hp > 0){//生存中だけ呼び出し
    party[num].Date.Atkfunction(party[num].AttackDate.Atk);
  }
}

function getPartyMemberSapportSkill(num){
    party[num].Date.Sapportfunction(party[num].SapportDate.Sap);
}

function getPartyMemberDamageSkill(num){
  party[num].Date.Dam();
}

function PartyStetasCheck(i){
        if(partystetas[i].Hp <= 0){
          party[i].Date.motionsprite=party[i].Date.deadSprite;

          $("#member"+(i+1)+"-icon").addClass("deth");

          $("#member"+(i+1)+"_stetas").css('color', 'Red');
          $("#HPbox_member"+(i+1)).removeClass("alive");
          $("#HPbox_member"+(i+1)).addClass("dead");

          $("#member"+(i+1)+"-icon").removeClass("partymembers-icon");
            partystetas[i].Hp++;
            /*蘇生処理*/
          if(partystetas[i].Hp > 0){

            partystetas[i].Hp = partystetas[i].maxHP/2;
            $("#member"+(i+1)+"-icon").addClass("partymembers-icon");
            $("#member"+(i+1)+"-icon").removeClass("deth");

            $("#HPbox_member"+(i+1)).addClass("alive");
            $("#HPbox_member"+(i+1)).removeClass("dead");

          }
          if(Math.abs(partystetas[i].Hp) <= partystetas[i].maxHP){
            memberView[i].iconimg.style.opacity=1-(-partystetas[i].Hp/partystetas[i].maxHP);
            memberView[i].hpvar.style.width = (-partystetas[i].Hp/partystetas[i].maxHP)*100 +"%";
          }
        }else{
          if(party[i].Date.motionsprite === party[i].Date.deadSprite){
            party[i].Date.motionsprite = party[i].Date.waitSprite;
          }
          if($("#HPbox_member"+(i+1)).hasClass("dead")){
              $("#HPbox_member"+(i+1)).removeClass("dead");
              $("#HPbox_member"+(i+1)).addClass("alive");
          }if( $("#member"+(i+1)+"-icon").hasClass("deth")){
            $("#member"+(i+1)+"-icon").addClass("partymembers-icon");
            $("#member"+(i+1)+"-icon").removeClass("deth");
          }

          $("#member"+(i+1)+"_stetas").css('color', 'Black');
          memberView[i].hpvar.style.width = (partystetas[i].Hp/partystetas[i].maxHP)*100 +"%";
        }
        if(partystetas[i].Hp > partystetas[i].maxHP)partystetas[i].Hp = partystetas[i].maxHP;
}

function Party_Update(){
  if(GameStage !== "GameOver"){
  for(i=0;i<4;i++){
      getPartyMemberSapportSkill(i);//Supportを毎時間呼び出す
      PartyStetasCheck(i);
      memberView[i].Hp.innerHTML=partystetas[i].Hp;
  }
  //全員の状態を調べる
    for(i=0;i<4;i++){
      if(partystetas[i].Hp > 0){
        break;
      }
    }if(i===4) GameStage="GameOver";
}}


function Party_Draw(){
  for(i=0;i<4;i++){//パーティメンバー描画
      Characterdtaw(140+markerdistance,45+i*20,party[i].Date.motionsprite);//パーティー
  }
}

この時、Atk:function()の中で書いている『characters["主人公"]』の部分を変数にして書きたいです。

やりたいこと

partyidをどのように記述して親オブジェクトから参照したらいいのかわかりません。partyidは他のオブジェクトも共通して持つプロパティであり、一々プロパティ名を指定し自身を参照するのはおかしい気がします。親オブジェクトの持つプロパティを取得するための代表的な記述や別の構造などあれば教えていただきたいです。よろしくお願いします。

試したこと

this.partyとするとunderfindeで取得できませんでした。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • mijinko889

    2018/11/13 00:03

    悩んでいた部分が本題とかけ離れそうなので一旦解決にしました。たくさんの知識をいただけて勉強になりました!キャラデータのポインタのみを管理する配列を別に作り、それをオブジェクトのselfに渡したりする関数をつくって動きそうです。具体的な構造の練り直しに繋がった方をBAにさせていただきました。回答ありがとうございます(;o;)

    キャンセル

  • think49

    2018/11/13 00:36

    "具体的な構造の練り直しに繋がった方をBAに" そういう切り捨てられ方で質問を閉められるとは思っていませんでした。解決の為に必要な「方法」は具体的に書いてるつもりなんですけどね。

    キャンセル

  • mijinko889

    2018/11/13 00:59

    本文の情報だけで様々な考え方を教えていただけたのは嬉しかったです。具体的な方法を実際に組み込むように考えた時に自分として考えやすかったものを検討しようと考えたんです。今回の質問回で活かし方がイマイチ分からなかったものも今後使用できるように勉強しようとも思っています。本題に対する個人の捉え方の違いや技術力の違いだと捉えていただきたいです。提示していただいたコードももちろん見ました。端的に言えば、自分が頭弱すぎて複雑なコードや話をまだ理解できなかったということです。BAを選ぶ基準も個人の感性によるかと思います。

    キャンセル

回答 2

checkベストアンサー

+2

こんにちは。

前後の背景を知らないと、最適なデータ構造がどういうものかはっきり言えませんが、とりあえずご質問に挙げられているコードの部分だけを見て局所的なリファクタをするとして、以下を一案として考えました。(ES6の classは使っていません。)

function AttackDate(discription, count, character) {
  this.discription = discription;
  this.count = count;
  this.character = character;
}

AttackDate.prototype.Atk = function() {
  const getDamage = partystetas[this.character.partyid].Pow;
  Damage(standEnemy[this.character.partyid], getDamage);
}; 

function Character(name, partyid) {
  this.name = name;
  this.partyid = partyid;
}

すなわち、コンストラクタAttackDate の引数として、character を受け取り、これをプロパティとして保持しておいて、AttackDateオブジェクトのメソッドからCharacterオブジェクトを使いたいときは、 this.character で参照します。

以下は、上記を使用したサンプルです。

ただし、上記のサンプルでは、とりあえず動くようにするため、ご質問に書かれていない以下を追加しています。

  • standEnemy: 長さ20で、要素が "A""B""C" のいずれかである配列

  • partystetas: 長さが20で、要素は 0以上9以下の整数であるプロパティ Powを持つオブジェクト

  • Damage(standEnemy, damage): 単純に2つの引数を console に出力する関数

以上、参考になれば幸いです。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/11/12 20:17

    なるほど!例を作っていただいてありがとうございます!
    こちらのAttackDate()に関数引数を設定するならばやはりコールバック処理で呼び出すべきでしょうか?

    キャラによって処理が違うことがあるのでprototypeでコールバック用の関数も作ろうと考えたのですが…

    キャンセル

  • 2018/11/13 12:05

    こんにちは。

    > こちらのAttackDate()に関数引数を設定するならばやはりコールバック処理で呼び出すべきでしょうか?
    >
    > キャラによって処理が違うことがあるのでprototypeでコールバック用の関数も作ろうと考えたのですが…

    の件ですが、できましたら、私が回答に挙げたサンプル

    https://jsfiddle.net/jun68ykt/e29ao7sm/8/

    をForkして頂き(一番上のForkというボタンクリック)、それに

    > AttackDate()に関数引数を

    追加して、

    > キャラによって処理が違うこと

    をやろうとしてみたコードを追加したものを作成して

    「こういうことがしたくて、こうしてみたが、このあたりがモヤモヤしてます。」

    みたいな感じでコメント頂けますと分かりやすいです。
    そこまで問題が明確になったら、別途の質問として投稿したほうが他の回答者さんの知恵も借りれるので、
    mijinko889さんにとってより解決が早いかもしれません。

    キャンセル

+2

 オブジェクトの親子

JavaScriptのオブジェクトには「親子」の概念がありません。
つまり、言語仕様の機能を使って親オブジェクトを参照する事は出来ません。

おそらく、DOM APIの parentNode プロパティを意識されているのだと思いますが、あれは初期化時に親ノードを設定する事で機能しています。
要素ノードBを要素ノードAの配下に appendChild する時、「appendChild が要素ノードBの parentNode プロパティ値を要素ノードAに書き換える」のです。

従って、appendChild と同じようにすれば、「親オブジェクトを参照する parent プロパティ」を作ることは可能です。

  • parent プロパティを初期化する汎用のオブジェクト挿入関数 appendObject() を定義する
  • createElement で生成した要素ノードが tagName を持つのと同じように、createCharacter() で生成したキャラクターが name プロパティを持つように作る

 疑似 DOM API

以前、作ったものがイメージに近いかもしれません(parentNode を使えます)。

 関数 Atk の場所

本題ではありませんが、Atk() の場所が気になります。
現在のコードでは、次のように呼び出しますが、

characters['主人公'].AttackDate.Atk();


攻撃の主体は「主人公」なので、

characters['主人公'].Atk();

にするのが自然に見えます。
(※主人公のスタンド能力でスタンド AttackDate が攻撃しているのなら、それでいいですが)

 オブジェクト初期化子 {}

変数 characters をオブジェクト初期化子 {} で初期化し、プロパティ名で「キャラクター名」を指定していますが、その場合はキャラクター名に __proto__ が使用出来ません。
任意の名前を持てるようにするには、new Map や多次元配列などの別の手段に変える必要があります。

Re: mijinko889 さん

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/11/12 20:07

    ありがとうございます!とても勉強になります!
    データを設定する処理を別に作って外部で設定するという考え方で合っていますか?

    new Character()時に親も設定するようにも検討しようと思います!ありがとうございます

    キャンセル

  • 2018/11/13 00:11 編集

    > データを設定する処理を別に作って外部で設定するという考え方で合っていますか?
    新規オブジェクト生成はコンストラクタで処理し、既存オブジェクトへの挿入は Foo.prototype.append() で挿入時に初期化というイメージですね。

    1. new CharacterList(iterableList) で初期化
    2. CharacterList.prototype.append(new Character) で (new Character).ownerList を初期化

    ここでいう new CharacterList は iterable にしておくと、for-of 等の反復処理が容易になります。
    https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Iteration_protocols

    キャンセル

  • 2018/11/13 00:23

    > 2. CharacterList.prototype.append(new Character) で (new Character).ownerList を初期化
    私の中では new CharacterList と new Character の関係は親子関係ではなく、所有関係にあると思うので、ownerList としています(Node#ownerDocument と同じ)。
    親子関係にするなら、new CharacterList と new Character が同じ性質を持ち、同じ parent, children プロパティを持つオブジェクトをプロトタイプチェーン上に持つよう設計してやる必要があります。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.57%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る