フィールドサイズは前作『VC』の約5倍と広大である。従来、それ1つでマップ全体であった規模の都市部は3つ存在し、その他に山岳地帯、砂漠、農村なども存在する。ゲームシステム上は都市を中心とした「ロスサントス (Los Santos)」「サンフィエロ (San Fierro)」「ラスベンチュラス (Las Venturas)」の3つのエリアに分けられるが、都市間の森林地帯や都市部に含まれない町村を主に指して、さらに5つの郡（カウンティ）に分けられる。広大になったが、本作においてはゲームエンジンの変更によって、前作でマップの移動の際に表示されていたデータロードがシームレス化され、自然な状態でのマップ移動が可能となっている。反面、建物に入る際にはロードが必要となった。「サンアンドレアス」自体は、初作『GTA』に登場するが、州ではなく都市であった（本作で言うサンフィエロのような街）。また、その名称は、それぞれの都市のモデルとなったロサンゼルス、サンフランシスコ、ラスベガスを横断するサンアンドレアス断層に由来している。
基本的には前作のシステムを踏襲しており、武器（アイテム）の種別が同じ場合は1種しか持つことができない。サイレンサー付き拳銃、短銃身ショットガン、熱追尾式ロケットランチャーなど新たに追加された武器も存在する。この他、アイテムとしてスプレー缶、消火器、パラシュートなどが追加されている。ガールフレンドへのプレゼント用として、花束とディルドも存在する。本作は、武器にも種別ごとのスキル値が存在し、武器の使用回数を重ねるなどしてスキルを上げることで連射速度・装填速度・命中精度を向上させたり、GTAシリーズ初二丁持ちといったことが可能となる。アクションについても大幅に増えている。ジャンプして壁や金網によじ登るといった基本動作の他に、各地のジムなどで覚えられる格闘攻撃が存在する。また、ナイフを装備した状態で相手の背後から忍び寄って攻撃すると、反撃されることなく一撃で殺害できる「ステルスキル」が発動する。
本作はプレーヤーの能力・外見に対しての自由度が大幅に上昇している。服装の自由度が大幅に向上した他、床屋で髪型を変えたり、身体にタトゥーを入れることが可能になった。また、従来のスタミナ値・肺活量に加えて、体脂肪量・筋肉量も新たに追加され、食事や運動の頻度によってこれら値が増減し、CJの能力・外見にも影響を与える。関連して空腹度（正確には最後に食事してからの経過時間）も存在し、定期的に食事をしないと体脂肪量・筋肉量の減少の他、最終的にライフも減っていってしまう。空腹によるライフ減少の結果ライフがなくなった場合は死亡する。セーブを行うか死亡などで病院からリスタートすると空腹はリセットされる（食事としてはカウントされない）。逮捕されても空腹はリセットされない。この他、ギャングメンバーを従えるリスペクト値や、ガールフレンドを作るために必要なセックスアピール値も存在する。
基本的に歩いたり、話していたりする。殴ったりすると反撃して来たり、逃げたりもする。今作では、女性の方が反撃してくる(ギャングを除いた一般市民)種類は普通の市民からヤクの売人、売春婦やたまにいきなり車両を奪う犯罪者、敵のギャング、など様々である。
上記 wiki_sample.y 前半に引き続き後半として記述すべき追加Ｃプログラム例は、以下のとおりである（ここではユーザがyylex()関数を手で書いているが、同じ機能のプログラムは、FLexに字句規則を与えて自動生成させた方がスマートであろう）。 int pass    = 0;   /* 通過回数．*/ int lineno  = 1;   /* 行番号．*/ int tokenno = 1;   /* 行内トークン番号．*/  /* 字句解析器 yylex．トークンを要求するときyyparseから呼び出される．    ここでは手で書いているが，Lex, Flexなどの字句解析器生成器を使えば自動生成が可能． */ int yylex(void) {    if (pass++ == 0) {        printf(" Calculator start."); prompt();    }    int c;    /* 空白やCrを読みとばす．*/    while ( (c = getchar() ) == ' ' || c == 0x0d || c == '\t'){    }    if (c == EOF) {     /* ファイル終端．コマンドプロンプトではCtrl-Dで入力可能. */        printf("  Bye.\n");        return 0;    }    if (c == '\n') {    /* 改行（NL）検出. */        lineno++;        tokenno = 0;        return EOL;    }    tokenno++;    if( isdigit(c) )  {        long long int val = (c - '0');        while ( isdigit(c = getchar() ) ) {            val  = val * 10 + c  - '0';        }         yylval.ldval = (long double)val;        ungetc(c, stdin);        //printf("  入力整数値 val=%lld, 意味値 yylval=%Lf, トークンコード NUM=%d\n",         //     val, yylval.ldval, NUM);        return(NUM);    }    /* printf("c=%d\n", c); */     if (c == '*') {        if ( (c = getchar() ) == '*') {            return EXPONENT;    /* 指数演算子の「**」だった．*/        }        ungetc(c, stdin);       /* 「**」ではなく乗算演算子の「*」だった．*/        c = '*';    }     /* その他のコードはそのまま伝え，'%' など直接文字コードで判定させている．*/    return(c);   }  /* 入力促進文字を表示する．*/ int prompt(void) {    printf( "\n ?> "); }  /* エラー発生時にyyparseから呼び出される．  * 引数: yyparseから渡ってくるエラーメッセージの文字列．  */ yyerror (char* str) {    printf (" Error! {%s}\n   lineno{%d} near tokenno{%d} yychar{%d} \n",             str, lineno, tokenno, yychar); }  /**    生成された構文解析器のメインプログラムになる．*/ int main(void) {    yydebug = 0;        /* Yacc動作をトレースしたいとき1にする．*/    yyparse();          /* 生成された構文解析関数を呼ぶ．*/ }